Skip to main content

Full text of "The PL/I programming language"

See other formats


COO-3077-151 


Courant  Mathematics  and 
Computing  Laboratory 

U.S.  Department  of  Energy 


The  PL/I  Programming  Language 


Paul  Abrahams 


Research  .\uc\  Development  Re|X)rt 
Prepared  under  Contract  EY-76-C-02-3077 
with  the  Office  of  Energy  Research 

Mathematic  s  cVk\  Computing 
March  1978 


New  York  University 


UNCLASSIFIED 

Courant  Mathematics  and  Computing  Laboratory 
New  York  University 


Mathematics  and  Computing      COO-3077-151 

THE  PL/I  PROGRAMMING  LANGUAGE 

Paul  Abrahams 

March  1978 


U.  S.  Department  of  Energy 
Contract  EY-76-C-02-3077 


UNCLASSIFIED 


This  report  is  to  appear  as  an  article  on  the  PL/I 
programming  language  in  the  Encyclopedia  of  Computer 
Science  and  Technology  published  by  Marcel  Dekker,  Inc, 


11 


TABLE  OF  CONTENTS 

Page 

INTRODUCTION 1 

Syntactic  Conventions 6 

DATA  TYPES 9 

Arithmetic  Types 9 

String  Types 11 

Pictured  Types 13 

Pointers ,  Areas  and  Offsets 19 

Files 20 

Labels 22 

Entries 23 

Formats 24 

Arrays 25 

Structures 26 

DECLARATIONS 29 

Manifest,  Explicit,  Contextual 

and  Implicit  Declarations 29 

Declarations  of  Statement-Names 31 

Attribute  Consistency  and  Completeness 32 

Standard  and  User  Defined  Defaults 38 

The  LIKE-Attribute 41 

EXPRESSIONS,  TYPE  CONVERSION,  AND  ASSIGNMENT 43 

Prefix  and  Infix  Expressions 45 

Builtin  Functions 51 

Type  Conversion 64 

Promotion 68 

The  Assignment-Statement 69 

STORAGE  TYPES 7  2 

Static  Storage 72 

Automatic  Storage 73 

Controlled  Storage 73 

Based  Storage 74 

The  Refer-Option 77 

Lef t-to-Right  Correspondence 79 

Allocation  in  Areas 80 

Parameter  Storage 81 

Defined  Storage 82 

Alignment 84 

Initialization 86 


111 


PROCEDURES,  SCOPES  AND  ENVIRONMENTS 8  8 

The  RETURN-Statement 90 

Arguments  and  Parameters 91 

Options 9  3 

Recursion 94 

The  GENERIC-Attribute 9  5 

Blocks  and  Scopes 96 

Internal  and  External  Scope 9  9 

Entry  Values  and  Environments 100 

ON-UNITS  AND  ON-STATEMENTS 102 

The  ON-Statement,  REVERT-Statement , 

and  SIGNAL-Statement 10  5 

Enablement  and  Disablement 10  8 

Builtin  Functions  for  ON-Conditions 109 

Categorization  of  the  ON-Conditions 110 

OTHER  STATEMENTS  AFFECTING  FLOW  OF  CONTROL 115 

Conditional  Statements 115 

The  DO-Statement 116 

The  GOTO-Statement 119 

The  STOP-Statement  and  the  Null-Statement 121 

FILES  AND  RECORD  INPUT-OUTPUT 122 

File  Attributes 122 

File  Opening  and  Attribute  Determination 123 

File  Closing 127 

Operations  on  Record  Files 127 

STREAM  INPUT-OUTPUT 135 

Data  Lists 137 

List-Directed  Input-Output 138 

Data-Directed  Input-Output 140 

Edit-Directed  Input-Output 142 

BIBLIOGRAPHY 150 


IV 


INTRODUCTION 

PL/I  is  a  large  and  powerful  multipurpose  prograiraning 
language.   The  intent  of  the  designers  of  PL/I  was  to  create 
a  language  that  could  be  used  in  business  and  in  scientific 
applications,  as  well  as  in  systems  programming  applications 
such  as  writing  operating  systems.   The  original  design  was 
developed  in  196  3  by  a  committee  of  people  drawn  from  IBM 
and  from  SHARE,  an  IBM  user   group.   For  a   long  time  the  only 
important  implementations  of  PL/I  were  those  developed  by  IBM 
on  the  360  and  370  computers,  and  the  implementation  on  the 
GE  645  at  the  MULTICS  Project  at  MIT.   However,  during  the 
early  1970' s  a  number  of  other  implementations  arose.   The 
implementation  of  PL/I  by  other  organizations  was  given  impetus 
by  the  development  of  a  national  and  international  standard 
for  PL/I  by  a  subcommittee  of  the  American  National  Standards 
Institute,  in  conjunction  with  a  similar  subcommittee  of  the 
European  Computer  Manufacturers'  Association. 

The  definition  of  Standard  PL/I  was  formally  released 
late  in  1976,  but  the  content  of  the  standard  was  publicly  known 
well  before  then.   The  standard  itself  was  written  in  a  novel 
manner  as  a  set  of  algorithms,  expressed  in  highly  stylized 
English,  for  the  operation   of  a  hypothetical  PL/I  machine. 
The  version  of  PL/I  described  in  this  article  is  Standard  PL/I. 

The  design  of  PL/I  drew  heavily  on  the  major  languages 
that  existed  in  1963:   Fortran,  Cobol ,  and  Algol  60.   The 
syntax  of  PL/I  most  resembles  that  of  Fortran,  but  without 


Fortran's  rigid  rules  for  program  formatting.   The  notion  of 
block  structure  was  taken  from  Algol  60,  while  PL/I  structures 
were  taken  from  the  record  descriptions  of  Cobol.   However,  a 
great  many  features  were  added  to  PL/I  that  have  no  counter- 
part in  its  ancestor  languages. 

An  example  of  a  PL/I  program  is  given  in  figure  1.   A 
program  is  written  as  a  sequence  of  external   procedures ,    which 
are  defined  in  such  a  way  that  they  can  be  compiled  separately 
and  then  linked  together  when  the  program  is  executed.  Within 
an  external  procedure,  there  can  be  internal   procedures . 
In  this  example,  there  is  one  internal  procedure,  named 
GET_DIGRAM.   Each  procedure  in  the  program  constitutes  a  block, 
In  addition,  a  block  can  be  delimited  by  the  PL/I  statements 
BEGIN  and  END  (as  in  Algol  60) . 

The  internal  procedure  GET_DIGRAM  communicates  with  the 
outer  procedure   DIGRAMS  via  arguments  passed  to  GET_DIGRAM 
by  the  CALL  statement.   From  the  viewpoint  of  GET_DIGRAM, 
these  arguments  appear  as  parameters  and   are  listed  in  the 
PROCEDURE- statement . 

The  variables  used  in  this  program  are  given  in  the 
DECLARE-statements.   In  general,  a  variable  (or  other  use  of 
an  identifier)  is  described  by  a  set  of  attributes .      Not  all 
of  these  need  be  given  in  the  DECLARE- statement;  those  that 
are  not  given  are  deduced  through  the  application  of  a  set  of 
defaulting  rules.   In  fact,  defaulting  is  applied  in  a  great 
many  contexts  within  PL/I. 


On  account  of  its  comprehensive  nature,  PL/I  is  a  diffi- 
cult language  to  learn  in  its  entirety.   For  that  reason  it 
was  designed  so  that  a  user  could  learn  just  those  parts  of 
the  language  that  he  needed,  and  ignore  the  rest  of  it  until 
the  occasion  arose  to  use  some  previously  untried  feature. 
The  extensive  defaulting  conventions  were  included,  for  a 
large  part,  to  make  it  possible  to  write  programs  without 
having  to  learn  about  obscure  and  irrelevant  attributes.  For 
instance,  one  can  write  business  programs  in  PL/I  without 
ever  realizing  that  the  language  includes  complex  numbers  and 
an  extensive  repertoire  of  mathematical  builtin  functions. 

Since  Standard  PL/I  is  intended  to  be  implemented  on  a 
variety  of  machines,  the  standard  provides  that  a  number  of 
characteristics  of  the  language  are  implementation-defined. 
For  example,  machines  differ  in  their  word  lengths;  therefore 
the  maximum  number  of  digits  that  need   be  carried  in  a 
floating  point  conputation  is  left  implemented-def ined .   In  the 
description  of  PL/I  given  in  this  article,  implementation-defined 
features  of  the  language  are  referred  to  frequently. 

As  of  this  writing,  a  standard  subset   version  of  PL/I 
is  under  development  by  the  American  National  Standards  Insti- 
tute.  The  definition  of  this  standard  subset  will  probably  be 
released  by  the  time  that  this  article  appears.  Moreover,  an 
extension  of  the  subset   to  include  facilities  for  real  time 
and  concurrent  programming  is  also  under  development  by  the 
same  group. 


Figure  1.    A  Sample  PL/I  Program 

/*   THIS  PROGRAM  READS  IN  A  TEXT  AND  COUNTS  THE  NUMBER  OF  TIMES 
THAT  EACH  ALPHABETIC  DIGRAM  OCCURS.   A  DIGRAM  IS  A  SEQUENCE 
OF  TWO  ADJACENT  CHARACTERS.   FOR  INSTANCE,  THE  DIGRAMS  IN 
'GRUNGE"  ARE  GR,  RU ,  UN,  NG ,  AND  GE . 
*/ 
DIGRAMS:  PROCEDURE  OPTIONS  (MAIN) ; 

DECLARE  COUNT(26,26)  FIXED(4); 

/*  COUNT (I, J)  GIVES  THE  CURRENT  COUNT  OF  OCCURRENCES 

OF  THE  DIGRAM   FORMED  FROM  THE  I-TH  LETTER  AND 
'   THE  J-TH  LETTER. 

/* 

DECLARE  ALPH  CHARACTER (26 )  INITIAL 

(  'ABCDEFGHIJKLMNOPQRSTUVWXYZ'  ); 
DECLARE  (P1,P2)  FIXED;  /*  ALPHABETIC  POSITION  OF  LETTER  */ 
DECLARE  (L1,L2)  CHARACTER ( 1 ) ; 
/*  FIRST  AND  SECOND  LETTERS  OF  DIGRAM  */ 
DECLARE  DONE  BIT(l)  INITIAL (' 0 ' B) ; 
/*  COMPLETION  FLAG  */ 

/*  SET  ALL  ELEMENTS  OF  THE  COUNT  ARRAY  TO  ZERO  */ 

COUNT  =  0; 

/*  READ  AND  PROCESS  DIGRAMS  */ 

RDLOOP:   DO  WHILE ('I'B);   /*  DO  FOREVER  */ 
CALL  GET_DIGRAM(L1,L2,D0NE) ; 
IF  DONE  THEN 

GO  TO  PRINT; 
PI  =  INDEX (ALPH, LI ) ; 
P2  =  INDEX (ALPH, L2) ; 

/*  N.B.— INDEX  RETURNS  ZERO  IF  LETTER  NOT  IN  ALPHABET  */ 

IF  PI  *  P2  >  0  THEN    /*  DIGRAM  IS  ALPHABETIC  */ 

C0UNT(P1,P2)  =C0UNT(P1,P2)+1; 
END  RDLOOP; 

/*  PRINT  THE  RESULTS  */ 

PRINT:    DO  PI  =  1  TO  26; 

DO  P2  =  1  TO  26; 

IF  COUNT (PI, P2)>0  THEN   /*  DIGRAM  APPEARED  */ 
PUT  EDIT(SUBSTR(ALPH,P1,1) , 
SUBSTR(ALPH,P2,1) ,  COUNT (PI ,P2) ) 
(SKIP, 2  A(l)  ,  X(2)  ,  F(4))  ; 
END; 
END ; 

STOP;   /*  END  EXECUTION  OF  PROGRAM  */ 


Figure  1.   Continued 


/*   INTERNAL  PROCEDURE  TO  EXTRACT  THE  NEXT  PAIR 

FROM  THE  INPUT  TEXT 
/* 

GET_DIGRAM:  PROCEDURE (LI , L2 , FLAG) ; 

DECLARE (LI, L2)  CHARACTER ( 1 ) ;   /*  LETTER  PAIR  */ 
DECLARE  FLAG  BIT(l);   /*  END-OF-DATA  INDICATOR  */ 
DECLARE  CARD  CHARACTER ( 80 ) ;    /*  INPUT  LINE  IMAGE  */ 
DECLARE  POSN  FIXED  STATIC  INITIAL(80); 

/*  CHARACTER  POSITION  IN  INPUT 

CARD  */ 
DECLARE  SYS IN  RECORD  INPUT  FILE; 

/*  INPUT  READ  FROM  FILE  SYSIN  */ 
ON  ENDFILE (SYSIN)     /*  WHEN  INPUT  EXHAUSTED  */ 

GO  TO  INPUT_FINISHED; 
IF  POSN  >  79  THEN  DO ; 

READ  FILE(SYSIN)   INTO(CARD);   /*  READ  A  CARD  */ 
POSN  =1;   /*  PROCESS  FROM  START  OF  CARD  */ 
END; 
LI  =  SUBSTR(CARD,  POSN,  1); 
L2  =  SUBSTR(CARD,  POSN+1,1); 
POSN  =  POSN+1;   /*  MOVE  TO  NEXT  PAIR  */ 
RETURN;  /*  EXIT  FROM  THIS  PROCEDURE  */ 

/*   COME  HERE  IF  THE  READ  STATEMENT  ENCOUNTERED  AN  END  OF  FILE  */ 

INPUT_FINISHED: 

FLAG  =  'I'B;     /*  SIGNAL  COMPLETION  TO  CALLER  */ 

RETURN ; 

END  GET_DIGRAM; 

END  DIGRAMS; 


Syntactic  Conventions 


A  PL/I  external  procedure  consists  of  a  sequence  of 
statements .      With  the  exceptions  of  the  IF- statement  and  the 
ON- statement ,  every  statement  is  followed  by  a  semicolon. 
The  program  is  presented  in  free  field  format,  i.e.,  statements 
do  not  occupy  a  fixed  position  on  the  line.   In  fact,  line 
boundaries  are  ignored  altogether,  so  a  statement  can  be  split 
over  several  lines,  or  several  statements  can  occupy  a  single 
line.   With  the  exception  of  the  assignment  statement  and  the 
null  statement,  the  type  of  a  statement  is  indicated  by  the 
keyword   with  which  it  begins. 

A  statement,  in  turn,  is  written  as  a  sequence  of  tokens 
each  of  which  may  be  either  a  delimiter   or  a  nondelimiter. 
The  types  of  tokens  are : 


Delimiters 
operator 
period 
comma 

left  or  right  parenthesis 
colon 
semicolon 
text  inclusion 


Nondelimiters 
identifier 
arithmetic  constant 
string  constant 
isub  [discussed  below] 


Two  adjacent  nondelim.iters   must  have  at  least  one  blank 
between  them,  and  other  than  that  adjacent  tokens  may  have 
any  number  of  blanks  between  them.    For  example  the  statement 


DO  IVAL  =  Q  TO  (A  +  3) ; 
could  be  written  more  compactly  as 

DO  IVAL^Q  T0(A+3)  ; 
but  it  could  not  be  further  condensed  to 

D0IVAL=QT0(A+3) ; 
The  last  example  is  in  fact  a  valid  PL/I  statement  with  an 
entirely  different  meaning.   A  comment   may  be  used  in  any 
place  where  a  blank  can  appear.   A  comment  is  written  as  the 
characters    ■■/*"   followed  by  a  sequence  of  characters 
not  containing   " */"  ,  followed  by   " */"  ,  e.g., 

/*  THIS  IS  A  COMMENT  */ 
An  identifier  consists  of  a  letter  followed  by  any  number  of 
letters,  digits,  and  break  characters   "_"  ,  e.g., 
P0PE_LE0_THE_15TH.   The  syntax  of  most  of  the  other  kinds  of 
tokens  is  discussed  below. 

A  text    inclusion   has  the  form 

%INCLUDE  textname  ; 
The  textname    refers  to  an  externally  stored  piece  of  text, 
which  replaces  the  text  inclusion  when  the  program  is  trans- 
lated.  On  account  of  the  variation  in  operating  environments 
on  different  machines,  the  interpretation  of  the  textname    is 
implementation  defined.   With  this  facility,  it  is  possible 
to  use  the  same  version  of  a  chunk   of  program  in  many  differ- 
ent external  procedures,  even  ones  written  by  different 
programmers . 

Keywords  can  be  used  as  identifiers;  in  this  respect  PL/I 
differs  from  COBOL,  which  treats  keywrods  as  reserved  words, 

7 


and  from  ALGOL,  which  uses  a  distinct  typeface  to  represent 
them.   For  example, 

IF  IF  THEN  THEN  =  ELSE  ; 
is  a  valid  sequence  of  statements  in  PL/I.   The  first  statement 
is  an  IF  statement  that  tests  the  variable  IF;  the  second   is 
an  assignment  statement  whose  target  is  the  variable  THEN. 
Most  of  the  long  key  words  can  be  abbreviated,  e.g. ,  CTL  for 
CONTROLLED  or  NOFOFL  for  NOFIXEDOVERFLOW.   The  names  of  some 
builtin  functions  can  also  be  abbreviated. 

There  are  three  different  kinds  of  PL/I  statements  that 
head  groups  of  statements:  the  DO  statement, the  PROCEDURE 
statement,  and  the  BEGIN  statement-   For  all  three,  the  group 
is  ended  by  an  END  statement.   If  a  statement  name  is  attached 
to  the  statement  that  heads  a  group,  then  the  same  statement 
name  can  also  be  attached  to  the  END  statement  that  terminates 
the  group,  thus  indicating  which  statement  is  closed  out  by 
the  END.   A  single  END  statement  can  close  out  more  than  one 
group,  however,  as  the  following  example  illustrates: 

ALEPH:   PROCEDURE; 
DO; 

BEIT:    BEGIN; 

END  BEIT; 
END  ALEPH; 

The  final  END  statement  closes  out  both  the  leading  PROCEDURE 
statement  and  the  DO  statement  that  follows  it. 


DATA  TYPES 

The  different  kinds  of  data  in  PL/I  can  be  classified 
into  groups  called  data   types,    or  simply  types.      The  avail- 
able types  are  either  aggregate   types   or  saalar   types.      An 
aggregate  is  composed  from  simpler  types,  and  can  be  either 
an  array  or  a  structure.   Arrays  and  structures  are  discussed 
below.   The  scalar  types  can  be  grouped  into  printable   and 
nonprintable    types,  sometimes  known  as  computational      and 
nonaomputational.       For  each  type,  there  can  be  variables  and 
values  of  that  type;  for  some  types  there  can  also  be 
constants . 

A  constant  associates  a  naime  with  a  single  unchanging 
value,  while  a  variable  associates  a  name  with  a  location 
where  a  value  can  be  stored.   The  value  of  a  variable  is  in 
general  time-dependent.   Variables  are  introduced  into  the 
program  by  DECLARE-statements ,  e.g., 

DECLARE  LETTER_SEQUENCE  CHARACTER (15)  VARYING; 

which  declares  the  variable  LETTER_SEQUENCE  to  have  character 
strings  of  length  from  0  to  15  as  its  values.   However,  a 
variable  can  be  declared  even  though  no  DECLARE- statement  is 
written  for  it  (see  "Declarations"  below) ,  and  certain  kinds 
of  constants  are  also  introduced  through  DECLARE-statements. 

Arithmetic  Types 

The  printable  types  consist  of  the  arithmetic  types  and 
the  string  types.  The  arithmetic  types  are  characterized  by 
four  kinds  of  attributes:   the  base    (binary  or  decimal) , 


the  scale     (fixed  or  float) ,  the  mode    (real  or  complex)  and 
the  preoision.       Since  all  combinations  of  base,  scale,  and 
mode  are  permitted,  there  are  eight  arithmetic  types,  pre- 
cision aside.   The  precision  of  a  fixed  type  consists  of  a 
number- of- digits   and  a  scale- factor ;    that  of  a  float  type 
consists  just  of  the  number- of- digits.   For  example,  the 
type  REAL  FIXED  DECIMAL (6, 2)   (the  "(6,2)"  indicates  the 
precision)  contains  values  of  the  form  +  DDDD.DD,  where  the 
D's  are  decimal  digits.   If  the  scale  factor  is  omitted,  it 
is  taken  as  zero.    The  binary  types  are  similar,  except 
that  binary  rather  than  decimal  digits  are  used.   The  fixed 
and  float  types  correspond  to  the  fixed  point   and  floating 
point  arithmetic  data  available  on  most  computers.   FIXED  is 
a  generalization  of  the  integer   type  found  in  a  number  of 
other  programming  languages,  since  the  integer   type  does  not 
provide  for  scaling.   FLOAT  corresponds  to  the  real    type  of 
other  languages.   A  notable  feature  of  the  float  types  is 
that  they  can  be  used  to  express  the  desired  accuracy  of  a 
numerical  computation  independently  of  the  word  length  of 
the  computer  carrying  out  the  computation. 

The  type  of  an  arithmetic  constant  is  indicated  by  its 
form.  A  real  fixed  decimal  constant  consists  of  a  sequence 
of  decimal  digits  with  an  optional  decimal  point  and  sign, 

e.g.,  -  17.76  .   Fixed  constants  can  be  scaled;  for  instance, 

-4 
78F-4  has  the  value   0.0078,  obtained  by  multiplying  78  by  10 

A  real  float  decimal  constant  consists  of  a  real  fixed  decimal 

constant  followed  by  an  exponent  part  indicated  by  the  letter  E, 

10 


e.g.,  4.832E+12.   Binary  constants  are  formed  similarly, 
except  that  only  binary  digits  are  used,  and  the  number  is 
followed  by  the  letter  B,  e.g.,  10. IB  or  11E16B  (designating 
3  X  10   ) .   Complex  constants  do  not  exist  as  such;  a  complex 
constant  is  formed  as  the  sum  of  a  real  constant  and  an  imag- 
inary constant,  e.g.,  4+31. 

String  Types 

The  string  types  are  aharaoter   and  bit,    each  of  which 
in  turn  may  be  varying   or  nonvarying .      However,  string  values 
are  sequences  of  characters  or  bits,  and  the  varying  and  non- 
varying  attributes  are  not  applied  to  them.   Each  string  type 
has  a  maximum  length  associated  with  it;  for  the  nonvarying 
types,  the  actual  length  is  always  equal  to  the  maximum 
length.   For  instance,  the  type  CHARACTER (14 )  VARYING  describes 
character  strings  whose  length  varies  from  0  to  14  characters, 
while  the  type  BIT (8)  NONVARYING  (NONVARYING  is   the  default) 
describes  bit  strings  that  are  always   exactly  8  bits  long. 
A  character-string  constant  is  written  as  a  sequence  of 
characters  enclosed  in  single  quotes,  with  internal  quotes 
doubled,  e.g., 

'THE  FARMER' 'S  DAUGHTER' 

The  null  character  string,  which  contains  no  characters,  is 
written  as  ' ' .   A  bit-string  constant  is  written  as  a  sequence 
of  binary  digits  enclosed  in  single  quotes  and  followed  by  B, 
e.g.,  'lOlOOl'B;  the  null  bit  string  is  written  as  ''B.  Bit 
strings  can  also  be  written  in  base-4,  base-8,  or  base-16 

11 


notation.   For  instance,  '7400'B3  indicates  the  base-8  (octal) 
constant  7400  (equivalent  to  ' 111100000000 ' B) ,  while  'A81'B4 
indicates  a  base-16  constant.   (The  digit  after  the  B  indicates 
a  power  of  2.)   This  extended  notation  is  not  available  for 
binary  arithmetic  constants.   The  one-bit  values  'I'B  and  'O'B 
are  particularly  useful,  as  they  are  the  results  returned  by 
the  PL/I  comparison  operators;  'I'B  represents  true    and  'O'B 
represents  false. 

When  a  string,  either  bit  or  character,  is  declared,  the 
maximum  length  need  not  be  given  by  a  constant,  so  that  the 
declaration 

DECLARE  NEWSTR  CHARACTER (Kl+2 ) ; 

is  permissible.   The  expression  Kl+2  must  be  well-defined  at 
the  time  that  NEWSTR  is  created.   Strings  that  appear  as 
parameters  of  procedures  may  have  their  maximum  lengths  given 
by  * ,  e . g . , 

DECLARE  PARAM_3  CHARACTER (*) ; 

In  this  case,  the  maximum  length  of  PARAM_3  is  determined  by 
the  argument  corresponding  to  PARAM_3 .   Strings  used  as  para- 
meters must  have  their  maximum  lengths  given  either  by  *  or 
by  an  expression  composed  purely  of  constants;  more  general 
expressions  are  not  permitted  (but  are  not  particularly  useful 
in  this  context  in  any  case) . 


12 


Pictured  Types 

The  pictured   types  are  derived  from  similar  types  in  COBOL, 
but  are  more  general,   A  pictured  type  has  an  associated  picture, 
e.g.,  999V. 99,  that  describes  the  appearance  of  the  values  of 
that  type.   The  values  are  represented  as  character  strings,  and 
the  semantics  of  PL/I  are  such  that  an  implementation  is  actually 
obliged  to  store  them  that  way.   There  are  no  constants  of 
pictured  type.   Pictures  can  be  used  in  input-output  formats  as 
well  as  in  declarations;  an  example  of  the  declaration  of  a 
pictured  type  is 

DECLARE  SALARY  PICTURE  ' $$$ , $$$V . $$ ' ; 

A  pictured  type  has  a  picture  associated  with  it.  The 
picture  is  given  by  a  character  string.   Within  the  picture, 
parenthesized  counts  can  be  used  to  indicate  repeated  characters, 
so  that  the  picture  •$$$$$'  can  also  be  written  as  '(5)$'. 
A  picture  can  be  either  a  character  picture  or  a  numeric 
picture.   A  numeric  pictured  type  contains,  in  addition  to  the 
picture  itself,  a  mode  specification  (either  REAL  or  COMPLEX) . 

Character   pictures  are  rather  simple.  They  consist  of 
just  the  characters  A,  9,  and  X.   The  character  A  stands  for 
a  letter  or  a  blank;  the  character  9  stands  for  a  digit  or  a 
blank;  and  the  character  X  stands  for  anything.   Character 
pictures  are  used  to  validate  strings,  i.e.,  to  insure  that 
they  are  in  the  proper  form.   Thus,  if  we  have  the  declaration 

DECLARE  STRING_TEST  PICTURE  'OJAXXg'; 

we  can  assign  to  STRING_TEST  values  consisting  of  three  letters 

13 


(or   blanks)  followed  by  any  two  characters   followed  by  a 
single   digit  (or  blank) .   If  the  assigned  value  does  not 
have  these  characteristics,  an  error  will  be  signalled. 

A  niimeric  picture  is  one  that  contains  a  character  other 
than  A,  9,  or  X.   By  this  definition,  a  picture  consisting  of 
all  9's  is  a  character  picture  rather  than  a  numeric  picture. 
A  numeric  pictured  type  has  an  associated  arithmetic  type, 
which  is  determined  by  the  form  of  the  picture  together  with 
the  mode.   The  picture  itself  is  independent  of  the  mode. 
Thus  if  we  have  the  declarations 

DECLARE  RPIC  REAL  PICTURE  'ZZZ'; 
DECLARE  CPIC  COMPLEX  PICTURE  'ZZZ'; 

RPIC  has  an  associated  arithmetic  type  of  REAL  FIXED  DECIMAL (3 , 0) , 
while  CPIC  has  an  associated  arithmetic  type  of  COMPLEX 
FIXED  DECIMAL(3, 0) .   (The  associated  arithmetic  type  is  neces- 
sarily decimal.)   If  the   picture  contains  either  of  the 
characters  E  or  K,  it  is  a  float  picture  (i.e.,  its  associated 
arithmetic  type  is  float) ;  otherwise,  assuming  it  is  numeric, 
it  is  a  fixed  picture.   The  associated  arithmetic  type  can  be 
thought  of  as  specifying  the  meaning  of  the  values,  as  distinct 
from  the  representation  of  the  values.   The  meaning  becomes 
important  when  pictured  values  are  used  in  arithmetic  operations. 

When  a   numeric  value  is  assigned  to  a  pictured  variable, 
the  value  is  edited  to  conform  to  the  picture.   The  characters 
in  the  picture  determine  how  the  editing  is  to  be  done,  assuming 
that  the  value  has  already  been  converted  to  the  associated 


14 


arithmetic  type.   Editing  by  means  of  a  picture  is  illustrated 
by  the  following  example:   Suppose  that  the  value  34.8  is  to 
be  edited  using  the  picture  S9999V.99.   Then  the  pictured  value 
will  be  +0034.80.   In  this  example  the  S  indicates  an  explicit 
sign,  the  9's  indicate  explicit  digits,  the  V  indicates  an 
implicit  decimal  point  (used  to  align   the  numeric  value  with 
the  characters  of  the  picture)  and  the  period  is  an  insertion 
character.   The  meanings  of  the  different  picture  characters 
are  given  in  Table  1,  and  their  use  is  illustrated  in  Table  2. 
The  I,  R,  and  T  characters  represent  digits  with  sign- 
overpunching,  i.e.,  the  sign  of  the  entire  value  is  combined 
with  the  digit  to  form  a  single  character.   Sign-overpunching 
is  the  standard  input  convention  for  COBOL;   on  a  keypunch, 
the  characters  are  formed  by  punching  both  a  sign  (11-row  for  -, 
12-row  for  +)  and  a  digit  in  a  single  column.   Although  CR  and 
DB  are  two  characters  rather  than  one,  the  two  characters 
always  go  together,  and  are  used  to  indicate  a  negative 
quantity.   CR  stands  for  "credit"  and  DB  for  "debit". 

The  drifting  characters  $,  Z,    +,    -,    and  S  are  used  to  edit 
leading  zeros  into  blanks.   When  a  sequence  of  drifting  charac- 
ters appears  (they  must  all  be  the  same  one) ,  the  character 
drifts  to  the  position  to  the  left  of  the  leading  nonzero  digit 
in  the  value,  and  the  remaining  positions  to  the  left  are 
blanked  out.   Any  insertion  characters   within  the  blanked-out 
positions  are  themselves  blanked  out. 

The  period  is  a  true  insertion  character,  in  that  its 
presence  or  absence  has  no  effect  on  the  value  represented  by 

15 


the  picture.   The   period  need  not  appear  next  to  the  V,  even 
though  the  sequence  "V."  is  often  used.   A  single  $,  S,  +,  or  - 
can  be  placed  at  the  beginning  or  at  the  end  of  a  picture,  in 
which  case  it  signifies  an  explicit  insertion  rather  than  a 
drifting  position.   For  instance,  the  value  92  edited  by  the 
picture  •(5)$V.$$S'  yields  the  string  '\6}6$92.00+'  ;    in  this 
case  the  S  causes  a  sign  to  be  inserted,  and  is  not  a  drifting 
character. 

Although  pictures  are  ordinarily  used  to  edit  real  values, 
they  can  be  used  to  edit  complex  values  also.   When  a  variable 
is  declared  to  be  pictured  and  complex,  the  picture  is  used 
to  edit  both  the  real  and  imaginary  parts  of  any  complex  value 
assigned  to  the  variable,  and  the  two  parts  are  concatenated. 
However,  there  is  no  way  to  use  pictures  to  insert  "I"  into 
the  edited  representation  of  a  complex  number. 


16 


Table  1.   Meaning  of  Picture  Symbols 

(a)  Character-picture  symbols 

A       alphabetic  character 

9       digit  or  blank  permitted 

X       anything  permitted 

(b)  Numeric-picture  symbols 

9       digit 

Y  digit  with  zero  mapped  to  blank 
Z       digit   with  zero-suppression 

$  drifting  or  inserted  dollar  sign 

*  drifting  asterisk  (check  protection) 

+  drifting  or  inserted  sign  for  positive  values 

drifting  or  inserted  sign  for  negative  values 

S  drifting  or  inserted  sign  for  all  values 

CR  credit  symbol,  inserted  for  negative  values 

DB  debit  symbol,  inserted  for  negative  values 

I  digit  with  positive  value  indicated  by  overpunch 

R  digit  with  negative  value  indicated  by  overpunch 

T  digit  with  sign  always  indicated  by  overpunch 

inserted  period 

,  inserted  comma 

B  inserted  blank 

/  inserted  slash 

V  implied  decimal  point 

E       start  of  exponent,  E  inserted 

K       start  of  exponent,  nothing  inserted 


17 


Table  2.   Examples  of  Numeric  Pictures 


Picture 

Numeric   Value 

Pictured   String 

999 

7 

007 

9  9V9 

7 

070 

YY/YY/YY 

760404 

76/)zJ4/)z54 

ZZZZ 

23 

)z5)zJ2  3 

Z,ZZZ 

123 

]6]6123 

Z,ZZZ 

1234 

\61,234 

Z.VZZS 

-1.6 

1.60- 

Z.VZZ+ 

.03 

)zJlz503+ 

ZV.ZZ+ 

.03 

]6.03  + 

-ZZZZS 

0 

\6fo]6]6ii6jp 

-ZZZZ$ 

2 

]6]6m2$ 

+ZZZZ$ 

2 

+)6]6]62$ 

ZZ99 

0 

]6\600 

$$$v.$$ 

1.23 

b$1.23 

$$B$$$ 

2345 

$2}zJ345 

$$B$$$ 

345 

]6]6$345 

+++ 

6 

]6+6 



6 

ms 

sss 

6 

]6+e 

$***Y^  ** 

.07 

$***.07 

$***^Y** 

.07 

5****07 

$$$99CR 

8 

]6]6$08W 

$$$99CR 

-123 

Jz$$123CR 

$$$99DB 

-123 

tJ$123DB 

999T 

71 

07A 

999T 

-71 

07J 

1999 

1776 

A776 

1999 

-1776 

1776 

ZZZR 

71 

71 

ZZZR 

-71 

7J 

99.V999BKS99 

123456 

12.346)z$+04 

V.99999E99 

123 

.12300E03 

999V9F3 

12345 

0123 

99999F-2 

12.34 

01234 

Note:      A   indicates 

1   with   postiive 

overpunch. 

J  indicates  1  with  negative  overpunch, 


18 


Pointers,  Areas  and  Offsets 

The  nonprintable  types  of  data  in  PL/I  are  pointers,  areas, 
offsets,  files,  labels,  entries,  and  formats.   A  pointer   can 
be  thought  of  as  the  location  of  a  piece  of  data;  it  resembles 
the  ref    (reference)  notion  of  Algol  68.   However,  pointer 
variables  in  PL/I  are  untyped;  that  is,  a  pointer  variable 
can  contain  a  pointer  to  data  of   any  type  whatsoever.  The 
only  pointer  constant  is  the  null  pointer,  which  does  not 
point  at  anything  and  therefore  does  not  corpare  equal  to  any 
pointer  to  an  existing  object.   The  null  pointer   is  obtained 
as  the  value  of  the  builtin  function  NULL  of  no  arguments. 
Pointers  are  used  in  conjunction  with  based  variables,  which 
act  as  templates  for  an  area  of  storage.   Based  variables  are 
discussed  below. 

An  area    is  a  region  in  which  space  for   based  variables 
can  be  allocated.   Areas  can  be  cleared  of  their  allocations 
in  a  single  operation,  thus  allowing  for  wholesale  freeing. 
Moreover,  areas  can  be  moved  from  one  place  to  another  by 
means  of  assignment  to  area  variables,  or  through  input-output 
operations.    There  is  one  area  constant,  the  empty  area, 
which  is  obtained  as  the  value  of  the  builtin  function  EMPTY 
of  no  arguments.  Assignment  of  the  empty  area  to  any  area 
variable  clears  the  area  of  its  allocations.  More  precisely, 
the  old  value  of  the  variable  is  destroyed  (though  a  copy  may 
exist  elsewhere) ,  and  the  new  value  is  an  area  with  nothing 
allocated  in  it.   The  declaration  of  an  area  specifies  (at 

19 


least  by  default)  an  area  size  in  implementation-defined  units 
(bytes,  words,  etc.),  e.g., 

DECLARE  STRUCTURE_AREA  AREA ( 2000 ); 

When  an  area  is  moved,  pointers  to  objects  within  the  area 
lose  their  validity.  Therefore,  PL/I  also  provides  offsets, 
which  are  pointers  relativized  to  the  origin    of  a  given  area. 
When  an  area  is  moved,  the  offsets  of  the  objects  within  the 
area  remain  unchanged.   Conceptually,  pointers  and  offsets  are 
related  by  the  equation 

pointer  =  offset  +  area 

but  in  an  actual  implementation  that  equation   need  not  hold. 
There  is  one  offset  constant,  the  null  offset,  which  is  obtained 
by  converting  the  null  pointer  to  an  offset. 

To  make  it  easier  to  work  with  offsets,  it  is  possible 
to  declare  an  offset  with  an  implicit  area  association  (which 
can  be  overridden),   e.g., 

DECLARE  0FF_FR0M_A3  OFFSET (A3); 
DECLARE  A3  AREA(300); 

When  0FF_FR0M_A3  is  referenced  in  a  context  where  a  pointer  is 
required,  the  offset  value  in  0FF_FR0M_A3  is  converted  to  a 
pointer  relative  to  the  area  A3. 

Files 

A  file    is,  conceptually,  a  port  through  which  communication 
is  established  between  the  program  and  a  dataset .      A  dataset, 
in  turn,  is  a  collection  of  information  residing  on  an  external 

20 


medium,  accessible  to  the  program  only  through  input-output 
operations.   During  the  course  of  execution  of  a  program, 
a  given  file  may  be  connected  to  different  datasets ,  or  to 
no  dataset,  at  different  times.   Input-output  operations 
reference  a  file,  which  must  be  connected  to  an  appropriate 
dataset;  the  operations  then  take  place  on  the  dataset.  A  file 
connected  to  a  dataset  is  said  to  be  open;    one  not  connected 
to  a  dataset  is  said  to  be  alosed. 

The  file  itself  is  a  file  value;  each  file  value  is 
uniquely  associated  with  a  file  constant,  declared,  for  example, 
by 

DECLARE  FILECON  FILE  CONSTANT; 

File  variables  are  declared  similarly,  e.g.,  by 

DECLARE  FILEVAR  FILE  VARIABLE; 

If  neither  CONSTANT  nor  VARIABLE  is  specified  in  the  declaration, 
the  usual  default  is  CONSTANT.   Moreover,  declarations  of  file 
constants  are  introduced  implicitly  in  a  number  of  contexts, 
so  that  in  practice  the  programmer  rarely  needs  to  write  these 
declarations.   For  instance,  the  PL/I  statement 

PUT  LIST(A,B) ; 

causes  the  values  of  A  and  B  to  be  written  onto  the  dataset 
associated  with  the  file  named  SYSPRINT  (assumed  since  no  other 
file  was  specified  in  the  PUT-statement) .   If  no  declaration  is 
explicitly  given  for  SYSPRINT,  the  declaration 

DECLARE  SYSPRINT  FILE  CONSTANT: 
21 


is  assumed.  When  the  PUT-statement  is  executed,  the  file 
SYSPRINT  is  opened  as  a  PRINT  file  (assuming  it  is  not  already 
open) . 

Labels 

A  label    is  a  name  attached  to  an  executable  statement  so 
that  control  can  be  transferred  to  that  statement  by  means  of 
a  GOTO- statement .   A  label  value  has  two  components:  a  designator 
(such  as  an  address)  of  the  statement  named  by  the  label,  and 
an  environment ,    which  records  the  state  of  execution  of  the 
program  at  the  time  when  the  block  containing  the  label  was 
entered.   The  environment  is  necessary  because  the  address  by 
itself  does  not  always  provide  sufficient  information  to  deter- 
mine unambiguously  the  state  of   execution  after  a  GOTO- statement 
has  been  carried  out  (see  the  section  "Entry  Values  and  Environ- 
ments" below) . 

Label  constants  are  declared  by  the  appearance  of  a  label 
as  a  statement-name ,    as  in 

BOOK_FOUND:  VOLUME  =  FOLIO (J) ; 

which  declares  BOOK_FOUND  as  a  label  constant.   In  fact,  label 
constants  cannot  be  declared  in  any  way  other  than  by  their 
appearance  as  statement-names.   Not  all  statement-names  declare 
label  constants,  however;  some  of  them  declare  entry  constants 
and  format  constants.   The  type  of  constant  declared  by  a 
statement-name  is  determined  by  the  type  of  statement  to  which 
it  is  attached.    Label  variables  are  declared  using  the 
attribute   LABEL,  e.g., 

22 


DECLARE  LABVAR  LABEL  VARIABLE; 

A  statement-name  can  be  written  with  one  or  more  subscripts, 
and  by  this  convention   constant  arrays  of  labels  can  be  created. 
For  instance,  if  a  block  contains  executable  statements  with  the 
statement-names  CASE(-l),  CASE(O),  CASE(l),  and  CASE (2),  the 
appearance  of  these  statement-names  constitutes  a  declaration 
of  CASE  as  a  constant  array  of  labels,  whose  single  subscript 
has  a  lower  bound  of  -1  and  an  upper  bound  of  2 .   It  is  quite 
permissible  to  attach  several  of  these  subscripted  statement- 
names  to  a  single  statement,  to  give  them  in  nonincreasing 
order,  to  attach  them  to  statements  also  bearing  nonsubscripted 
statement-names,  or  even  to  omit  some  index  values  from  the  set. 
For  instance,  if  CASE(O)  were  omitted  from  the  above  set,  the 
set  would  still  be   valid,  but  a  transfer  to  CASE(O)  would  be 
in  error.   Typically,  an  element  of  a  constant  array  of  labels 
is  selected   by  a  statement  such  as 

GO  TO  CASE(CASE_NUMBER) ; 

Entries 

An  entry    is  an  entry  point  to  a  procedure  (see  "Procedures, 
Scopes,  and  Environments"  below)  treated  as  a  datum.   An  entry 
constant  is  declared  by  the  appearance  of  a  statement-name 
attached  to  a  PROCEDURE-statement  or  an  ENTRY-statement .  For 
instance, 

PROCESS_NAME:   PROCEDURE (NAME, SPECS) ; 
declares   PROCESS_NAME  as  an  entry  constant,  whose  associated 

23 


value  is  the  (single)  entry  point  to  the  procedure  headed  by 
the  PROCEDURE-statement.   Entry  values,  like  label  values, 
carry  environments  with  them.   Entry  variables  and  arrays  of 
entry  constants  are  available.   A  typical  application  of  an 
entry  variable  arises  in  writing  a  procedure  to  integrate  an 
arbitrary  function;  within  the  procedure,  the  function  is 
declared  as  an  entry  variable  (and  a  parameter) ,  and  the 
actual  function  to  be  integrated  is   passed  as  an  argument. 

When  an  external  procedure  references  an  entry  in 
another  external  procedure,  then  the  first  procedure  must 
declare  the  second  explicitly  as  an  entry  constant,  as  in 

DECLARE  SEARCHVAL  EXTERNAL  ENTRY (CHARACTER (*) ) 
RETURNS (FIXED) ; 

The  CONSTANT  attribute  is   assuined  by  default  in  this  case. 
The  procedure  containing  this  declaration  expects  SEARCHVAL 
to  be  an  entry  to  an  external  procedure,  whose  expected  argu- 
ment is  a  character  string  of  unspecified  length,  and  which 
returns  a  fixed  value. 

Formats 

A  format    is  used  to  specify  the  form  of  data  on  a  dataset 
accessed  through  a  stream  file  (see  "Edit-Directed  Input-Output" 
below) .   A  format  constant  is  declared  by  the  appearance  of  a 
statement-name  on  a  FORMAT- statement ,  e.g., 

FMT3:  FORMAT (SKIP, 3A, X (M) , A) ; 

As  with  labels  and  entries,  format  variables  and  arrays  of 

format  constants   are  included  in  PL/I.   Format  variables 

24 


are  declared  using  the  attribute  FORMAT,  e.g., 

DECLARE  FMTVAR  FORMAT  VARIABLE; 

Since  formats  can  contain  references  to  variables,  e.g.,  the 
M  in  the  example  FMT3  above,  format  values  carry  environments. 

Arrays 

Two  types  of  aggregates  are  provided  in  PL/I:  arrays 
and  struGtures .      An  array  is  a  collection  of  elements  all  having 
the  same  type;  a  particular  member  of  the  collection  is  selected 
using  an  appropriate  sequence  of  subscripts.   A  structure,  on 
the  other  hand,  is  a  collection  of   elements  having  possibly 
different  types;  a  particular  member  of  the  collection  is 
selected  by  using  an  appropriate  name  as  the  selector.  A 
powerful  feature  of  PL/I  is  that  it  allows   aggregates  to  be 
treated  as  data  objects  in  most  contexts,  so  that  it  is 
possible,  for  instance/  to  add  two  arrays  in  a  single  opera- 
tion, or  to  write  a  procedure  that  returns  a  structure  as  its 
value . 

An  array  is  characterized  by  a  sequence  of  dimensions; 
the  dimensionality   of  the  array  is  the  number  of  its  dimensions. 
Each  dimension  has  a  lower  bound  and  an  upper  bound,  and  these 
can  be  arbitrary  integers.   For  example,  the  declaration 

DECLARE  MESH(-100: 100,200)  FLOAT  BINARY(40); 

declares  MESH  to  be  a  two-dimensional  array.   The  first  sub- 
script has  lower  bound  -100  and  upper  bound  100,  while  the 
second  has  lower  bound  1  (assumed  since  none  is  given)  and 

25 


upper  bound  200.   When  reference  is  made  to  an  element  of  an 
array,  the  subscripts  can  be  arbitrary  expressions,  as  long  as 
the  values  of  those  expressions  can  be  converted  to  integers. 

As  with  character  strings,  bounds  can  be  given  by  expres- 
sions as  well  as  constants.   For  an  array  parameter,  a  pair  of 
bounds   (not  a  single  one)  can  be  given  by  *.   The  *  can  also 
be  used  in   a  quite  different  sense  to  indicate  a  cross-section 
of  an  array.   For  instance,  MESH (3 ,* )  designates  a  one-dimais  ional 
array,  with  lower  bound  1  and  upper  bound  200,  consisting  of 
those  elements  of  MESH  whose  first  subscript  is  3.   Any  number 
of  the  siabscripts  in  an  array  reference  can  be  replaced  by  *  '  s  ; 
the  dimensionality  of  the  resulting  array  is  the  number  of  *'s. 

Array  variables  take  on  array  values .   Array  values  can 
arise  during  the  evaluation  of  an  expression,  e.g.,  when  two 
arrays  are  added  together.   Aside  from  labels,  entries,  and 
formats,  there  are  no  array  constants  in  PL/I. 

Structures 

A  structure  is  a  collection  of  named  elements,  each  of 
which  can  itself  be  a  structure.  An  example  of  a  two-level 
structure  declaration  is 

DECLARE 

1   EMPLOYEE_RECORD, 
2   NAME, 

3   FIRST  CHARACTER (10)  VARYING, 
3   MIDDLE_INITIAL  CHARACTER ( 1) , 
3   LAST  CHARACTER (15)  VARYING, 
2   ID_NUMBER  FIXED  DECIMAL(9), 
2   SALARY  FIXED  DECIMAL ( 7 , 2) ; 


26 


The  number  in  front  of  each  component  is  a  level  number.  The 
structure   as  a  whole  is  at  level  one.   The  members  of  the 
level-one  structure  are  the  level-two  components;  those  of 
the  level- two  components  are  the  level-three  components,  etc. 
It  is  possible  to  write  structure  declarations  using  nonconse- 
cutive  level  numbers,  but  there  is  always  an  equivalent 
structure  using  consecutive  ones.   In  any  event,  the  logical 
levels  are  always  consecutively  numbered. 

The  organization  of  a  structured  value  is  just  like  that 
of  a  structured  variable.   A  structured  value  contains  a  number 
of  components,  and  can  be  treated  as  a  single  object.  For 
example,  two  structures  can  be  added  just  as  two  arrays  can  be 
added.   There  are  no  structure  constants. 

The  elements  of  a  structure  are   referred  to  using 
qualified   names,    although  abbreviated  versions  are  permissible. 
The  fully-qualified  names  of  the  elements  of  the  structure 
given  above  are: 

EMPLOYEE_RECORD   (itself  a  structure) 
EMPLOYEE_RECORD.NAME    (itself  a  structure) 
EMPLOYEE_RECORD. NAME. FIRST 
EMPLOYEE_RECORD . NAME . MIDDLE_INITIAL 
EMPLOYEE_RECORD . NAME . LAST 
EMPLOYEE_RECORD . I D_NUMBER 
EMPLOYEE_RECORD . SALARY 

Abbreviated  versions  of  qualified  names  are  obtained  by  leaving 
out  any  of  the  component  identifiers  other  than  the  last  one. 

27 


These  abbreviated  forms  can  be  used  as  long  as  the  result  is 
not  ambiguous,  i.e.,  as  long  as  it  cannot  refer  to  more  than 
one  object. 

Arrays  of  structures  and  structures  of  arrays  are  possible, 
and  can  be  nested  to  any  depth.   An  example  of  such  a  nested 
structure  is  given  by: 

DECLARE 

1   CAR (30) , 

2   COUNTRY_OF_ORIGIN  FIXED  DECIMAL(3), 
2   DEALERS (40) , 

3   CITY  CHARACTER (20)  VARYING, 

3   STATE  CHARACTER (2) , 

3   COMPANY  CHARACTER (30)  VARYING; 

In  such  a  nested  entity,  dimensionality  is  inherited.   Thus 
CITY  is  a  two-dimensional  array  since  one  dimension  is  inherited 
from  CAR  and  the    other  from  DEALERS.   CITY(*,17)  designates 
the  array  composed  of  the  elements 

CAR(l).  DEALERS.  CITY (17) 
CAR(2).  DEALERS.  CITY(17) 

CAR (30) .DEALERS.  CITY(17) 

In  writing  a  reference   to  an  element  of  CITY  or  a  similar 
object,  the  subscripts  can  be  written  in  any  position  as  long 
as  they  are  in  the  right  order.   Thus  CITY(*,17)  could  also 
have  been  written  as  CAR(*)  .CITY ( 17)    or  as  DEALERS (*, 17)  .CITY . 


28 


DECLARATIONS 

A  declaration    associates  a  set  of  attributes   with  an 
identifier.   The  attributes  specify  the  characteristics  of  the 
object  denoted  by  the  identifier,  such  as  its  data  type. 
Within  an  external  procedure,  a  given  identifier  can  be  declared 
more  than  once,  since  the  identifier  can  be  declared  in  differ- 
ent blocks,  or  as  a   member  of  different  structures,  or  both. 
Nevertheless,  each  declaration  of  an  identifier  designates  a 
distinct  object  having  its  own  attributes.   The  rules  for  name 
resolution  determine  how  an  occurrence  of  an  identifier  is 
resolved  to  the  appropriate  declaration  (see  "Blocks  and  Scopes" 
below)  . 

Every  occurrence  of  an  identifier  within  an  external 
procedure  must  have  a   corresponding  declaration  within  that 
external  procedure.   Moreover,  every  declaration  must  have  a 
complete  and  consistent  set  of  attributes.   To  satisfy  these 
principles,  PL/I  includes  extensive  conventions  for  defaulting 
declarations,   i.e.,  for  creating  declarations  that  were  not 
written  in  the  program  and  for  deducing  attributes  of  incomplete 
declarations. 

Manifest,  Explicit,  Contextual,  and  Implicit  Declarations 

An  identifier  that  is  declared  in  a  DECLARE-statement 
written  by  the  programmer  is  said  to  be  man-i  ;'-r  ^ '' u   declared. 

*  The  term  "manifest",  though  convenient,  is  not  standard 
usage . 


29 


An  identifier  that  is  manifestly  declared,  or  that  appears  in 
a  parameter  list,  or  appears  as  a  statement  name,  is  said  to 
be  ex-plioitly   declared.   An  identifier  appearing  in  a  parameter 
list  may,  but  need  not,  be  manifestly  declared;   an  identifier 
appearing  in  a  statement-name  must  not  be  manifestly  declared. 

If  an  identifier  appears  in  a  procedure  and  no  explicit 
declaration  exists  for  that  identifier,  then  a  default  declara- 
tion is  created  if  possible.   The  default  declaration  is  placed 
in  the  outermost  block  of  the  external  procedure.   If  the 
identifier  is  used  in  such  a  way  as  to  suggest  what  its 
attributes  should  be,  it  is  said  to  be  contextually   declared; 
otherwise  it  is  implicitly    declared.   For  instance,  if  the 
identifier  OUT  has  not  been  manifestly  declared  and  the  state- 
ment 

PUT  FILE (OUT)  LIST(VALX,VALY) ; 

appears,  then  OUT  will  be  contextually  declared  with  the  attri- 
butes FILE  and  CONSTANT.   Similarly,  if  the  identifier  EXP  has 
not  been  manifestly  declared  and  the  statement 

Y  =  EXP(A**2)  ; 

appears,  EXP  will  be  contextually  declared  with  the  attribute 

BUILTIN.   A  program  is  in  error  if  it  induces  conflicting 

contextual  declarations. 

An  implicit  declaration    is  created  for  an  identifier 

if  it  is  declared  neither  explicitly  nor  contextually.  In 

this  case,  the  created  declaration  initially  has  no  attributes, 

and  all  the  attributes  are  added  by  default  later  on. 

30 


Declarations  of  Statement-Names 

The  appearance  of  an  identifier  as  a  statement-name 
completely  determines  its  attributes.   The  data  type  of  the 
identifier  is  determined  by  the  kind  of  statement  named,  as 
well  as  whether  or  not  the  identifier  is  subscripted.   If  the 
named  statement  is  an  ENTRY-statement  or  a  PROCEDURE-statement , 
the  identifier  acquires  the  attributes  ENTRY  and  CONSTANT;  if 
the  named  statement  is  a  FORMAT- statement  the  identifier 
acquires  the  attributes  FORMAT  and  CONSTANT;  and  if  it  is  any 
other  statement  the    identifier   acquires  the  attributes 
LABEL  and   CONSTANT.   Moreover,  if  the  statement-name  is  sub- 
scripted, then  the  identifier   acquires  an  appropriate 
DIMENSION-attribute . 

The  implied  declaration   of  an  entry  constant  cannot  be 
fully  constructed  until  the  types  of  its  parameters  are  known. 
When  the  parameter  types  are  known,  the  entry  constant  can  be 
characterized  completely.   At  that  point  the  parameter  speci- 
fications and  the  RETURNS-attribute ,  if  any,  are  added  to  the 
declaration.   For  instance,  the  statements 

FN:   PROCEDURE (A, B)  RETURNS (CHARACTER ( 12) VARYING) ; 
DECLARE  A  CHARACTER (*) ; 
DECLARE  B  POINTER; 

END  FN; 

lead  to  the  derived  declaration 

DECLARE  FN  ENTRY (CHARACTER (*), POINTER) 

RETURNS (CHARACTER (12)  VARYING)  CONSTANT; 


31 


Attribute  Consistency  and  Completeness 

The  diagram  of  Figure  2  can  be  used  to  determine  whether 
a  set  of  attributes  is  consistent  and  complete,  except  for  a 
few  peculiar  cases.   In  this  diagram,  the  following  conventions 
are  followed: 

1.  Double  lines  indicate  the  definition  of  a  term. 

2.  Rectangles  indicate  specific  attributes.   If  a  rectangle 
is  dashed,  it  indicates  an  optional  attribute. 

3.  Ovals  indicate  sets  of  attributes. 

4.  Horizontal  lines  indicate  sets  for  which  all  (nondashed) 
elements  must  be  present) . 

5.  Vertical  lines  indicate  sets  from  which  one  alternative 
must  be  chosen. 

A  set  of  attributes  is  complete  and  consistent  if  it  can  be 
constructed  from  this  diagram;  it  is  consistent  if  it  is  a 
subset  of  a  complete  and  consistent  set. 

The  interpretation  of  this  diagram  is  illustrated  by  the 
attribute  set 

INTERNAL  VARIABLE  AUTOMATIC  ALIGNED  POINTER  INITIAL 

Starting  from  the  node  "consistent-set",  a  scope  and  a  declara- 
tion-type must  both  be  chosen.   The  scope  can  be  either  INTERNAL 
or  EXTERNAL;  INTERNAL  is  chosen.   The  declaration-type  can  be 
a  variable,  a  named-constant,  etc.;  it  is  chosen  to  be  a 
variable.    In  that  case,  the  attribute  VARIABLE  must  be  present, 
as  well  as  a  storage-type  and  a  data-declaration.   The 

storage-type  is  chosen  to  be  a  storage-class;  the  storage-class 

32 


o' 


O 


03 
-P 
0) 

0) 
4-1 

XI 

■H 
>-l 
-U 
-P 

< 

4-) 
C 
0) 
4-1 

•rH 
C 

o 

U 

c 

rO 

0) 
4-1 

Q) 

i-H 

a 

e 


3 

•H 


Dd 

Q 

W 

U 

H 

a 

?=: 

U 

w 

H 

?: 

D3 

^ 

Q 

< 

s 

Aa 


Z 

z 

o 

M 

M 

Eh 

e-i 

U 

1— 1 

M 

Q 

D 

2 

CQ 

O 
U 

v^ 


0) 

a 
o 
u 


\^     S 


J 

J 

< 

< 

z 

•~r 

cc: 

a 

w 

CjJ 

H 

H 

Z 

X 

M 

DJ 

4J 
dJ 

I 

4-J 
C 

(U 
4J 

in 


c 

v8y 


01 
0} 
1T3 
rH 
O 
I 

<u 

en 
(0 
U 
O 
4-1 


^§ 


CJ 

Q 

H 

W 

Eh 

U 

M 

Q 

W 

1-^ 

S 

H 

.^_ 

W 

O 

o 

< 

< 

K 

H 

H 

CQ 

H 

P 

m 

2 

< 

O 
U 

en    I 
2 


2 

< 
Eh 
U^ 
2 
O 
U 


I 

c 

4-1 
0) 

c 
o 
u 
I 
n 

e 


o 

4-1 

a 

•H 

u 

0) 
0) 

I 

(1) 


vi/ 


>H 

a: 

U 

Eh 

1  1 

2 

M 

U 

iu 

ir< 

CQ 

O 

^ 

tu 

I  ^1 

E-« 

H 

X 


-a 

(U 

c 

•H 
■P 
C 

o 
u 


(1) 

•H 


TT 

a 

Di 

>i 

P 

4-1 

H 

W 

1 

•^- 

U 

« 

(0 

D 

M 

4J 

K 

1-1 

(0 

Eh 

TJ 

Ol 

^ 

O 

H 
M 

u 

w 


/^ 


r^ 


cr> 


(T3 


^ 

w 

hJ 

<JJ  . 

J 

cu 

T! 

^"" 

< 

— 

X 

O 

w 

o 

e 

Pi 

u 

o 

M 

tn 

W 
2 

H 
Q 


O 

■H 
4J 

a 

u 
en 

0) 

I 

rC 
-P 
CO 


^ 

■N 

( 

/^ 

rj 

^" 

1 — 

— 

1 

z 

X 

J 

o 

M 

w 

a 

)               5H 

rf: 

2 

>-l 

0) 

J 

h-l 

<          :£; 

?: 

M 

a 

•u 

< 

tx, 

a 

J    ^< 

i.. 

M 

^""  ^  ^ 

< 

u 

■;  W 

^ 

c 

)         z 

C) 

Di 

> 

b 

Di 

u 

u 

1               M 

Q 

< 
> 

z 
o 

o 

1 

z 

^^ 

N 

J 

J 

— 

y 

/ 

>. 

I 

_. 

FT  r- 

w 

t 

c 

D           Q 

H 

Eh 

oi 

t 

n          W 

< 

fc 

H 

U 

p 

f 

0     =  X 

^ 

O 

b— .  1- 

H  

^ 

H 

j: 

2              H 

J 

C 

u 

U 

frn 

Cn 

< 

•X, 

u 

H 

^ 

J  '-' 

'-"^n 

<^ 

>i 

-P 

-P 
1 

ex 
>1 

4J          / 

1      ( 

^              ( 

K, 

f 

"N 

rH 

0 

rH 

M 

Q. 

/^ 

C 

•H 
4-) 

(T3 

P 

c 

J- 

a) 

O 

•H 

to 
P 

O      _ 
•H       -    , 

5 

i-l 

4- 

c 

C 

Qj 

4-) 

3 

-P 

P 

4-> 

"■" 

>i 

(0 

a. 

(0 

H 

in 

c 

h 

+j 

— 

+J        - 

-  e 

4-) 

M 

1 
■u 

:3 

0 

u 
c 

3 

^    V 

-J 

o 

(0 

0 

0 

0 

I  "^J 

^ 

v^ 

I  u  ] 

34 


S' 
•Si 
I  ^1 


Q 

W 

w 


(1) 

c 

■H 
4-) 

c 
o 
u 


(N 

0) 


z 

o 

M 

El 
O 


Oi 

I 


a: 

2 


a. 

4J 
I 


c 
o 

■H 
4J 

(13 
-U 

D 

a 

E 
O 

o 


_< 


^ 


Eh  ' 
0.1 


ui    lui 
o      o 

I  I    I 


1-1 

c 


CJ 


a: 

u 

H 

H 

W 

►- 

7: 

— 

W 

M 

Uh 

O 

u. 

(X 

o 

o 


o 
+J 
(fl 
u 
o 


J  v^ 


35 


I  2 

W 

I  ^ 
'  2 

O 
I   °^ 

> 


I 

0 

4-1 

a, 

•H 

u 

10 

0) 
'O 

I 


\^ 


■^■ 

< 

M 

H 

H 

u 

2 

u 

W   - 

-  a 

3 

M 

a 

Q 

w 

CO 

J 

w 

Eh 

u  < 

Q 
Cl, 


-p 

4-1 

cu 

0) 

If) 

tn 

T1 

u 

(1) 

o 

S-l 

u 

-p 

(U 

w 

u 

w         ^-^ 

is  chosen  to  be  AUTOMATIC.   The  data-description  may,  but 
need  not,  contain  DIMENSION;  in  this  case  it  does  not  contain 
DIMENSION.   The  data-description  must  also  contain  an  alignment, 
chosen  to  be  ALIGNED,  and  either  a  data-type  with  optional 
INITIAL,  or  STRUCTURE.   In  this  case,  INITIAL  is  present 
(the  actual  initial  values  are  ignored) .   The  data-type  is 
chosen  to  be  noncomputational .   Of  the  alternatives  for  non- 
computational-type,  locator  is  selected;  of  the  alternatives 
for  locator,  POINTER  is  selected.   Thus  the  entire  set  of 
attributes  is  shown  to  be  complete  and  consistent.  (The  order 
in  which  the  attributes  are  given  is  immaterial.) 

Not  all  consistency  violations  are   shown  up  by  reference 
to  this  diagram.   For  example,  the  combination 

AUTOMATIC  EXTERNAL 

and  the  combination 

STATIC  LABEL  INITIAL 

are  both  invalid,  but  pass  the  test  of  the  diagram.   Specific 
auxiliary  rules  are  needed  in  order  to  disallow  these  and 
similar  cases.   The  requirement  for  completeness  is  relaxed 
for  the  case  of  file  constants,  i.e.,  declarations  with  the 
attributes  FILE  and  CONSTANT;   although  the  file-description-set 
must  be  consistent,  it  need  not  be  complete. 

Certain  attributes  either  permit  or  require  subspecif ica- 
tion;  these  are  listed  in  Table  3.   For  example,  the  BASED 
attribute  permits,  but  does  not  require,  the  subspecif ication 
of  an  auxiliary  pointer  (see  "Based  Storage"  below) ,  while  the 

36 


Table  3.  Attributes  Requiring  or  Permitting  Subspecif ication 


A.    Subspecif ication  Required 
Attribute 


CHARACTER 

BIT 

AREA 

DIMENSION 

RETURNS 

PRECISION 

PICTURE 

GENERIC 

ENTRY  with  CONSTANT 

POSITION 

DEFINED 

LIKE 


Required 
Specification 


length 

length 

size  of  area 

dimensionality 

type  of  returned  value 

number-of-digits,  possible  scale-factor 

picture  specification 

generic  specification 

parameter  types 

position  count 

base  variable 

likened  variable 


B.    Subspecif ication  Permitted 
Attribute 


Permitted 
Subspecif ication 


BASED 

OFFSET 

ENTRY  with  VARIABLE 


basing  pointer 

area 

parameter  types 


37 


CHARACTER  attribute  requires  the  subspecif ication  of  a  string 
length. 

The  ENTRY-attribute  and  the  RETURNS-attribute  themselves 
contain  attribute  sets  as  subspecif ications .   These  attribute 
sets  are  known  as  descriptor's ,    and  must  also  be  complete  and 
consistent.   The  descriptors  in  the  ENTRY-attribute  describe 
the  parameters   expected  by  a  procedure,  while  the  descriptors 

in  a  RETURNS-attribute  describe  the  value  returned  by  a  proce- 

* 
dure.   To  be  complete  and  consistent,  a  descriptor  must  be 

derivable  from  the  "data-type"  node  in  the  diagram  of  Figure  2, 

but  may  optionally  contain  the  MEMBER-attribute .  The  descriptor 

for  a  structure   as  a  whole  looks  like  the  declaration  of  a 

structure  variable  with  the  identifiers  left  off,  e.g., 

DECLARE  GENFUNC  ENTRY  ( 
1   (*)  , 

2   FIXED  BINARY, 

2   CHARACTER (30)  VARYING  ); 


Standard  and  User  Defined  Defaults 

The  PL/I  defaulting  rules  specify  how  an  incomplete  but 
consistent  set  of  attributes  is  to  be  completed.   Although 
there  is  a  standard  set  of  defaulting  rules,  the  DEFAULT- 
statement  can  be  used  to  override  them.   The  defaulting  rules, 
whether  standard  or  user-defined,  consist  of  a  predicate   and 
a  default   attribute    set.      The  predicate  indicates  a  test  to 
be  applied  to  the  attributes  already  present  in  the  declaration 


*   Since  procedures  can  accept  structures  as  arguments  and 
can  return  structures  as  values,  the  specification  of  a 
single  parameter  or  of  a  returned  value  can  contain  more 
than  one  descriptor. 

38 


(or  descriptor) ,  while  the  default  attribute  set  indicates 
additional  attributes  to  be  supplied  provided  that  they  are 
consistent  with  the  ones  already  present.   Inconsistency,  in 
this  case,  is  not  an  error;  it  simply  means  that  the  default 
is  not  applied. 

The  principal  standard   defaulting  rules  are: 

1.  Add  the  attributes  FIXED,  REAL,  and  BINARY.   For  instance, 

* 

if  FIXED  alone  is  present,  REAL  and  BINARY  are  added. 

2.  If  the  arithmetic  attributes  are  present  but  no  precision 
has  been  specified,  add  an  implementation-defined  precision 
whose  subspecif ication  depends  on  the  arithmetic   attri- 
butes already  present. 

3.  If  CHARACTER  or  BIT  is  present,  assume  NONVARYING. 

4.  If  CHARACTER  or  BIT  is  present  but  no  length  has  been 
specified,  assume  a  length  of  1 .   If  AREA  is  present  but 

no  area  size  has  been  specified,  assume   an  implementation- 
defined  value  for  the  area  size.   If  POSITION  is  present 
but  no  count  has  been  specified,  assume  a  count  of  1. 

5.  If  neither  VARIABLE  nor  CONSTANT  has  been  specified, 
assume  VARIABLE  unless  ENTRY  or  FILE  is  present.  If  ENTRY 
or  FILE  is  present  by  itself,  assume  CONSTANT.   If  ENTRY 

or  FILE  is  present  along  with  other  attributes,  the  default 
depends  on  what  those  other  attributes  are. 

6.  If  FILE  and  CONSTANT,  or  ENTRY  and  CONSTANT,  or  CONDITION 

is  present,  assume  that  the  scope  is  EXTERNAL;  in  all 

other  cases  assume   that  it  is  INTERNAL. 

'   This  rule  differs  from  the  one  used  in  the  well-known  IBM 
implementation  of  PL/I. 

39 


7.  If  EXTERNAL  is  present,  assume  that  the  storage  class  is 
STATIC;  in  all  other  cases  assume  that  it  is  AUTOMATIC. 

8.  If   CHARACTER  or  BIT  is  present,  assume  UNALIGNED; 
otherwise  assume  ALIGNED. 

As  a  consequence  of  the  defaulting  rules,  an  implicitly  declared 
identifier,  which  starts  with  an  empty  attribute  set,  will 
acquire  the  attribute  set 

REAL  FIXED  BINARY  PRECISION (d  , 0 )  VARIABLE  INTERNAL 
AUTOMATIC  ALIGNED 

Here  d,  is  the  implementation-defined  default  number-of-digits 
for  the  attribute  combination  FIXED  BINARY.   Similar  constants 
d.2,d^,   and  d   are  specified  for  FIXED  DECIMAL,  FLOAT  BINARY, 
and  FLOAT  DECIMAL. 

The  DEFAULT-statement  contains  a  predicate  and  one  or  more 
default  attribute  sets.   Declarations  are  completed  by  applying 
the  user-supplied  DEFAULT-statements  in  the  order  that  they 
appear  in  the  program,  and  then  applying  the  system  default 
rules.   Unlike  DECLARE-statements ,  DEFAULT-statements  are  order- 
dependent.   A  default  attribute  set  is  added  if  and  only  if 
the  appropriate  predicate  is  satisfied  and   none  of  the  elements 
in  the  set  leads  to  a  conflict.   The  predicate  can  include  both 
attributes  and  identifier  ranges,  indicated  by  the  keyword 
RANGE.   The  predicate  is  formed  as  a  boolean  combination  of 
attributes  and  ranges.   RANGE (*)  is  satisfied  by  any  identifier, 
but  not  by  a  descriptor.   RANGE (al;a2)  is  satisfied  by  any 


*  This  rule  does  not  apply  to  structures.  The  alignment  of  a 
structure,  if  given  explicitly,  is  passed  down  to  all  element- 
ary members  of  the  structure  unless  a  conflicting  alignment 
is  given  at  a  lower  level. 

40 


identifier  starting  with  a  letter  between  al   and  a2  inclusive, 
while  RANGE (init)  is  satisfied  by  any  identifier  starting 
with  init.      For  instance, 

DEFAULT  (RANGE  (AB)  I  FLOAT  &  ""BINARY)  COMPLEX  STATIC; 

applied  to  the  declaration 

DECLARE  AB35  FIXED; 
yields 

DECLARE  AB3  5  FIXED  COMPLEX  STATIC; 
and  applied  to  the  declaration 

DECLARE  XYZ  FLOAT  DECIMAL; 
yields 

DECLARE  XYZ  FLOAT  DECIMAL  COMPLEX  STATIC; 
However,  it  has  no  effect  when  applied  to 

DECLARE  AB35  REAL  FIXED; 
since  COMPLEX  conflicts  with  REAL.   It  also  has  no  effect  when 
applied  to 

DECLARE  XYZ  FLOAT  BINARY; 
since  the  predicate  is  not  satisfied. 

The  LIKE-Attribute 

The  LIKE-attribute  can  be  used  to  copy  part  of  a  structure 
declaration  into  another  declaration.   It  is  useful  when  a 
program  uses  many  similarly  organized  structures.   For  instance, 
if  a  program  includes  the  declarations 


41 


DECLARE 

1   ASSEMBLY  BASED, 

2   NEXT_PART  POINTER, 

2   FIRST_COMPONENT  POINTER, 

2   DESCRIPTION, 

3   PART_NUMBER  PICTURE  •X(5)9', 
3   COST  FIXED  DECIMAL ( 6 , 2) ; 
DECLARE  1  GROUP (20)  STATIC  LIKE  ASSEMBLY; 

then  the  second  declaration  is  equivalent  to 

DECLARE 

1   GROUP (20)  STATIC, 

2   NEXT_PART  POINTER, 

2   FIRST_COMPONENT  POINTER, 

2   DESCRIPTION, 

3   PART_NUMBER  PICTURE  'X(5)9*, 
3   COST  FIXED  DECIMAL ( 6 , 2 ) ; 

The  LIKE-attribute  causes  copying  of  members  only;  attributes 

at  the  level  of  the  LIKE-attribute  are  not  copied.   In  this 

example,  a  reference  to  GROUP (20) .NEXT_PART  requires  that  the 

qualifying  identifier  GROUP  be  included,  since  otherwise  the 

reference  would  be  ambiguous.   This  behavior  is  a  general 

property  of  structures  declared  using  the  LIKE-attribute. 


42 


EXPRESSIONS,  TYPE  CONVERSION,  AND  ASSIGNMENT 

The  kinds  of  expressions  acceptable  in  PL/I  are  similar 
to  those  found  in  most  higher-level  languages.  These  are: 
literal  constants 

references  to  variables  and  named  constants 
parenthesized  expressions 
function  calls 
prefix  expressions 
infix  expressions 
references  to  builtin  functions 

The  only  kinds  of  literal  constants  recognized  are  arithmetic 
constants  and  string  constants.   Other  constants  are  obtained 
either  as  named  constants,  e.g.,  statement-names,  or  as  the 
results  of  builtin  functions,  e.g.,  NULL.   References  to 
variables  may  have  subscripts,  pointer  qualifications  (see 
below),  and  name  qualifications.   An  example  of  a  reference 
with  all  three  is 

PT  ->  X(I, J) .  B 

Although  the  pointer-qualifier  symbol  ->  looks  like   an  operator 
it  is  not  treated  as  one  (see  "Based  Storage"  below) . 

Parentheses  are  used  within  expressions  in  three  ways: 
to  designate  subscripts  of  arrays,  to  designate  arguments  of 
functions,  and  to  group  components  of  expressions  containing 
operators.   When  an   expression  containing  operators,  e.g.,  + 
and  *,  is  enclosed  in  parentheses,  it  is   treated  as  a  single 


43 


entity  --  this  is  the   usual  convention  in  mathematical  nota- 
tion.  When  a  reference  to  a  variable  is  enclosed  in  paren- 
theses, it  is  then  treated  as  a  general  expression  rather 
than  as  a  variable.   This  rule  only  makes  a  difference  in 
the  context  of  a  procedure   call. 

A  function  call  has  two  parts:   the  reference  to  the 
function  and  the  argument  list.   The  function  reference  need 
not  have  the  form  of  a  single  identifier,  since  functions 
can  return  entry  values,  can  be  subscripted,  and  can  be 
members  of  structures.   Function  calls,  subscripted  references, 
and  references  to  builtin  functions  all  have  the  same  syntactic 
form,  so  a  knowledge  of  the  relevant  declarations  is  necessary 
in  order  to  distinguish  them.   An  example  of  a  rather  complex 
function  call  is 

A,B(3) (I, 'NEXT' ) (X) 

In  this  case,  A,B(3)  is  an  element  of  an  array  of  structures 
(or  a  structure  of  arrays)  containing  an  entry  value.  That 
entry  value  designates  a  procedure  that  expects  two  arguments 
—  in  this  case,  I  and  the  string  constant  'NEXT'  —  and 
itself  returns  an  entry  value.   The  entry  value  obtained  from 
this  second  procedure  is  then  applied  to  the  argument  X.   A 
function  expecting  no  arguments  is  called  by  using  an  empty 
argument  list.   Thus 

NEXT_SUIT(  ) 

would  call  the  procedure  NEXT_SUIT  with  no  arguments. 

If  E  is  a  procedure   that  itself  returns  an  entry  value,  then 

44 


F(E) 
indicates  that  F  is  to  be  called  with  an  argument  consisting 
of  the  entry  value  associated  with  E,  while 

F(E(  )) 
indicates  that  F  is  to  be  called  with  an  argument  obtained 
by  calling  E  as  a  function  of  no  arguments.  This  convention 
is  somewhat  different  from  the  one  used  in  Algol  60  and  many 
of  its  descendants. 

Function  calls  are  discussed  in  more  detail  under 
"Procedures,  Scopes  and  Environments"  below. 

Prefix  and  Infix  Expressions 

A  prefix   expression    consists  of  an  expression  preceded  by 
a  prefix  operator,  while  an  infix   expression   consists  of  two 
expressions  with  an  infix  operator  between  them.  When  an 
expression  contains  a  string  of  operators,  the  meaning  is 
determined  by  the  precedences  of  the  operators.   Those  opera- 
tors  with  highest  precedence  are  applied  first,  then  those  of 
next  highest  precedence,  etc.   The  infix  operators,  grouped 
by  precedence  from  high  to  low,  are: 

*   / 

+ 

II 

& 

I 

Table  4  summarizes  the  meanings  of  the  operators. 


45 


Table  4.   PL/I  Operators  and  Their  Meanings 


Infix  Operators 


* 

/ 

+ 


< 

> 

<  = 

>= 

-< 

-•> 


exponentiation 

multiplication 

division 

addition 

subtraction 

concatenation 

equal 

not  equal 

less  than 

greater  than 

less  than  or  equal 

greater  than  or  equal 

not  less 

not  greater 


Prefix  Operators 


minus 

plus 

not 


46 


When  a  sequence  of  adjacent  operators,  all  of  the  same  prece- 
dence, appears,  the   operators  are  applied  from  left  to  right 
except  in  the  case  of  **,  which  is  applied  from  right  to  left. 
Thus 

A*B/C**D**E 

is  interpreted  as 

(A  *  B)  /  (C  **  (D  **  E)  ) 

Prefix  operators  are  always  applied  first  unless  they  conflict 
with  the  **  operator;  in  that  case  the  **  is  applied  first, 
so  that 


is  interpreted  as 


even  though 


-  A  **  3 


-  (A  **  3) 


-  A  *  3 


is  interpreted  as 

(-  A)  *  3 

There  are  five  arithmetic  operators  in  PL/I: 

+   addition 

subtraction 
*   multiplication 
/   division 
**  exponentiation 

In  order  to  apply  any  of  the  first  four,  the  operands  must 

first  be  converted  to  a  common  base,  scale,  and  mode  according 

to  the  following  rules: 

47 


Base;   binary  if  either  operand  binary,  otherwise  decimal 
Scale:  float  if  either  operand  float,    otherwise  fixed 
Mode;   complex  if  either  operand  complex,  otherwise  real 

The  operands  need  not  have  the  same  precision,  however.  The 
rules  for  the  results  of  these  operations  assume  that  there 
is  a  maximum  value  N  for  the  number-of-digits  of  the  result. 
The  "precision  rules"  then  give  the  number-of-digits  (and, 
for  the  case  of  fixed,  the  scale-factor)  of  the  result. 
They  are  arranged  so  that  digits  on  the    right  are  never 
thrown  away  except  in  the  case  of  division,  where  it  cannot 
be  avoided.   If  the  operands  are  float,  then  the  number-of- 
digits  of  the  result  is  the  maximum  of  the  nuinbers-of-digits 
of  the  operands.   Otherwise,  assume  that  the  two  operands, 
which  are  necessarily  fixed,  have  precision  (p,q)  and  (r,s) 
respectively.   The  result  precision  (m,n)  for  the  four  opera- 
tions is  given  by: 

+,-     m  =  min(N,  max(p-q,r-s)  +  max(q,s)  +  1) 

n  =  max ( q , s ) 
*       m  =  min (N,p+r+l) 

n  =  q+s 
/       m  =  N 

n  =  N-p+q-s 

Should  the  result  value  exceed  the  capacity  of  the  result 
precision,  the  FIXEDOVERFLOW- condition  is  raised,  indicating 
an  error  (see  "ON-Units  and  ON-Statements"  below) . 

The  situation  with  regard  to  exponentiation  is  somewhat 
more  complicated.   Suppose  that  the   formula   x  **  y  is  being 


48 


computed.   If  either  x  or  y  is  float,  the  result  is  float, 
and  the  result  precision  is  that  of  p.   If  x  is  real  and 
fixed,  and  y  is  a  small  integer  constant,  then  the  result 
is  also  real  and  fixed,  and  the  precision  of  the  result 
is  given  by: 

m=  (p  +  1)  *y-l 
n  =  q  *  y 

In  any  other  case  where  both  x  and  y  are  real,  the  result  is 

real.    If  either  x  or  y  is  complex,  the  result  is  complex. 

In  certain  cases,  such  as  x  real  and  fixed  with  a  negative 

value  and  y  not  an  integer,  an  error  is  indicated. 

The  comparison  operators  are: 

=  equal 

"■=  not  equal 

<  less  than 

>  greater  than 

<=  less  than  or  equal  to 

>=  greater  than  or  equal  to 

"■<  not  less  than 

"■>  not  greater  than 

The  operators  <=  and  "">  are  equivalent,  as  are  the  operators 
>=  and  "■<  ;  "■>  and  ""<  are  included  mainly  for  intellectual 
compatibility  with  COBOL,  which  uses  the  phrases  NOT  GREATER 
and  NOT  LESS.   All  of  the  comparison   operators  return  a 
one-bit  value:  'I'B  if  the  comparison  is  satisfied,  and  'O'B 
if  it  is  not.   The  equality  and  inequality  comparisons  can  be 
applied  to  any  type  of  data,  although   for  most  of  the  non- 
printable  types  both  operands  must  have  the  same  type.  (The 

49 


only  exception  is  that   pointers  can  be  compared  to  offsets.) 

The  comparison   operators  that  test  for  inequality  cannot 
be  applied  to   complex  arithmetic  data  or  to  nonprintable  data, 
but  they  can  be  applied  to  real  arithmetic  data,  to  pictured 
data,  and  to  strings.   The  meanings  of   these  operators 
applied  to  arithmetic  data  are  the  usual  ones;  a  numeric 
pictured  datum  is  treated  as  the  numeric  value  that  it  repre- 
sents.  When  character  strings  of  unequal  length  are  compared, 
the  shorter  one  is  filled  on  the  right  with  blanks  to  bring  it 
to  the  same  length  as  the  longer  one.   The  ordered  comparisons 
are  then  done  left  to  right  on  the  basis  of  the  implementation- 
defined  collating  sequence,  which  defines  an  order  for  the 
individual  characters.  For  instance,  the  letters  are  ordered 
alphabetically  and  the    digits  numerically.  Two  character 
strings  compare  equal  if  they  are  identical  after  the  shorter 
one  has  been  blank-filled  on  the  right.   The  ability  to 
perform  ordered  comparisons  of  character  strings  is  particu- 
larly useful  in  applications  that  involve  sorting  names. 
Similar  rules  apply  to  bit  strings:   If  two  bit  strings  are 
of  unequal  length,  the  shorter  one  is  filled  on  the  right  with 
zero-bits  prior  to  the  comparison.   If  the  strings  differ,  the 
comparison  is  done  bit-by-bit  from  the  left  with  the  rule  that 
a  one-bit   is  greater  than  a  zero-bit. 

If  the  two  operands  of  a  comparision  have  different  types, 
they  are  converted  to  a  common  type.   If  one  has  an  arithmetic 
type,  the  other  is  converted  to  an  arithmetic  type.  If  neither 

has  an  arithmetic  type,  but  one  has  a  character  type  and  the 

50 


other  has  a  bit  type,  the  bit- type  operand  is  converted  to 
character. 

The  concatenation  operator,  "| |",  is  used  to  put  two 
strings  together.   For  instance,  the  value  of 

•AVER'  I  I  'AGE' 

is  the  string  'AVERAGE'.   If  one  operand  is  a  bit  string  and 
the  other  is  a  character  string,  the  bit  string  is  converted 
to  a  character  string  (as  it  is  for  comparisons) . 
There  are  also  three  logical  operators: 

&     and 

I 

""     not 

"&"  and  "I"  are  infix  operators,  while  """'  is  a  prefix  operator, 
The  operands  of  these  operators  are  expected  to  be  bit  strings, 
so  that  if  they  have  any  other  type,  they  are  conerted  to  bit 
strings . 

Builtin  Functions 

PL/I  includes  a  large  variety  of  builtin  functions.  A  list 
of  these,  together  with  a  brief  explanation  of  what  each  one 
does,  is  given  in  Table  5.   Some  of  the  more  important  builtin 
functions  will  now  be  described. 

The  first  group  of  builtin  functions  deals  with  strings. 
The  descriptions  of  the  functions  will  be  given  for  character 
strings,  but  the  definitions  for  bit  strings  are  analogous. 

The  builtin  function  LENGTH (x)  returns  as  its  value  the 

51 


actual  length  of  the  character  string  x.   It  is  useful   in 
two  contexts:   determining  the  length   of  a  string  passed  as 
a  parameter,  and  determining  the  current  length  of  a  varying 
string.   In  both  of  these  cases,  the  declaration  of  the 
string  does  not  provide  enough  information  to  determine  the 
length. 

The  builtin  function  SUBSTR  (x,  y_,  z)  is  used  to  extract  a 
portion  of  a  string.   x  is  the  string,  y  is  the  position  of 
the  first  character  to  be  extracted,  and  z^  is  the  number  of 
characters  to  be  extracted.   If  z^  is  omitted,  all  of  the  string 
starting  with  the  character  at  position  y.  is  extracted.  The 
null  string  is  a  possible  result.   SUBSTR  can  also  be  used 
on  the  left  side  of  an  assignment.  For  example,  given  the 
statements 

DECLARE  CHARS  CH ARACTER ( 8 ) ; 
CHARS  =  ' TOM  JONES ' ; 
SUBSTR(CHAR8,2,5)  =  'IMHA'; 

the  resulting  value  of  CHARS  is  'TIM  HANES ' .   A  builtin  function 
used  on  the  left  side  of  an  assignment  in  this  way  is  called 
a  pseudovariable. 

The  builtin  function  INDEX  (x,^;)  finds  the  first  position 
within  the  string  x  where  the  string  ^  occurs.  If  y.   does  not 
occur  at  all  within  x,  the  value  of  INDEX  is  0.  For  example: 

value  of  INDEX (' SYNCOPATION • ,  'COP')  =  4 
value  of  INDEX (' SYNCOPATION ' ,  'COPE')  =  0 

The  builtin  function  VERIFY (x,^)  finds  the  first  character  in 


52 


Table  5.   SUMMARY  OF  THE  PL/I  BUILTIN   FUNCTIONS 

In  this  table,  descriptions  of  the  various  PL/I  builtin 
functions  are  given.   These  descriptions  are  intended  to 
indicate  the  intent  of  each  function,  and  in  some  cases  the 
principal  restrictions  on  their  arguments.   In  the  descriptions 
of  the  functions,  square  brackets  are  used  to  indicate  optional 
arguments . 

1.  ABS(.r)   -  the  absolute  value  of  x.       If  x   is  complex, 
the  value  is  its  modulus. 

2.  ACOS(a')  -  the  arc  cosine  of  x.  x   must  not  be  complex. 

3.  KDT){x,y  ,p ,  \q])    -  the  sum  of  x   and  u   with  precision 

{p,q)    or  (r.O)  if  the  result  is  fixed,  and  with  precision 
(p)  if  the  result  is  float. 

4.  ADDR(.t)  -  a  pointer  to  the  generation  of  x. 

5.  AFTER (sa,ca)  -  the  portion  of  the  string  sa    that  follows 
the  first  occurrence  of  ca   within  sa.      If  aa      does  not 
occur  within  sa ,    the  value  is  the  null  string. 

6.  ALLOCATION (x)  -  the  number  of  generations  of  the  controlled 
variable  x    that  currently  exist. 

7.  ASIN(j:)  -  the  arc  sine  of  x.      x   must  not  be  complex. 

8.  ATAN  (j/ [,.r]  )  -  the  arc  tangent  of  y/x      if  x    is  given, 
and  of  1/  otherwise. 

9.  ATAND(!/[ ,  J-]  )  -  the  arc  tangent  in  degrees  of  y/x   if  x   is 
given,  and  of  y   otherwise.   x  and  y   must  be  real. 

10.  ATANH{x)  -  the  hyperbolic  arc  tangent  of  x. 


53 


Table  5.   Continued 

11.  BEFORE (sa,ea)  -  the  portion  of  the  string  sa    that  precedes 
the  first  occurrence  of  aa   within  sa .      If  ca   does  not 
occur  within  sa,    the  value  is  the  null  string. 

12.  BINARY  (x  [  ,p  [  ,(7]  ]  )  -  the  result  of  converting  x   to  binary, 
with  precision  determined  by  p  and  q    if  one  or  both  is  given. 

13.  BIT(j;,  [le])    -    the  result  of  converting  x   to  bit,  with 
length  le    if  le    is  given. 

14.  BOOL{x ,y , oa)    -    the  boolean  function  of  x   and  y   whose  truth 
table  is  specified  by  the  four-bit  value  ca. 

15.  CEIL(a;)  -  the  least  integer   greater  than  or  equal  to  x. 
X   must  be  complex. 

16.  CHARACTER  (sa,  [Z-e  ]  )  -  the  result  of  converting  sa    to  charac- 
ter, with  length  le    if  le    is  given. 

17.  COLLATE (  )  -  the  implementation-defined  collating  sequence, 
as  a  character  string. 

18.  COMPLEX  (x,  2/)  -  the  complex  number  x   +   i   y. 

19.  CONJG(x)  -  the  complex  conjugate  of  x. 

20.  COPY(sa,Ze)  -  the  string  consisting  of  le   copies  of  sa 
concatenated  together. 

21.  COS(a;)  -  the  cosine  of  x. 

22.  COSD(a:)  -  the  cosine  of  x,    with  x   given  in  degrees. 
X   must  not  be  complex. 

23.  COSH(x)  -  the  hyperbolic  cosine  of  x. 

24.  DATE (  )  -  the  current  date,  in  the  form  yymmdd,    where 
yy    is  the  year,  mm   is  the  month,  and  dd   the  day. 


54 


25.  DECAT {sa, ca , pa)    -    a  portion  of  the  string  sa.    sa    is 
partitioned  into  three  pieces  by  the  first   occurrence 
of  aa.      The  three-bit  string  pa    specifies  which  of  the 
three  pieces  (before  oa,    aa    itself,  after  ca)    are  to  be 
concatenated  to  form  the  value  of  DECAT. 

26.  DECIMAL {x [ ,p [ ,^] ] )   -  the  result  of  converting  x   to  deci- 
mal, with  precision  determined  by  p  and  q  if  one  or  both 
is  given. 

27.  DIMENSION (x, n)  -  the  number  of  elements  in  the  n-th 
dimension  of  the  array  x,    defined   as  HBOUND(x,n)- 
LBOUND(x,n)  +  1. 

28.  DIVIDE  (x,i/ ,p  [,q])    -      the  quotient  of  x    and  y   with 
precision  {p,q)    or  (p,0)  if  the  result  is  fixed,  and 
with  precision  (p)  if  it  is  float. 

29.  DOT{x,y     [  ,p  I  ,q] ])    -    the  dot  product  of  x   and  u,    with 
precision  determined  by  p    and  q    if  one  or  both  is  given. 

30.  EMPTY (  )  -  the  empty  area-value. 

31.  ERF(x)  -  the  statistical  error  function  of  x. 

32.  ERFC(x)  -  the  complanent  of  the  statistical  error  func- 
tion of  X. 

33.  EVERY(x)  -  the  value  'I'B  if  every  bit  in  x  is  a  one-bit, 
and  'O'B  otherwise.  For  this  purpose,  all  scalar-elements 
of  X   are  converted  to  bit, 

34.  EXP(x)  -  the  exponential  function   of  x. 

35.  FIXED  (x,  p[,q])    -    the  result  of  converting  .r  to  fixed, 
with  precision  determined  by  p  and,  if  it  is  given,  ,. 

36.  FLOAT (x,p)  -  the  result  of  converting  x  to  float  with 

precision  (p) . 

55 


37.  FLOOR(x)  -  the  greatest  integer  less  than  or  equal  to  x. 
X   must  be  real. 

38.  HBOUND(a:,n)  -  the  upper  bound  of  the  n-th  dimension  of 
the  array  x. 

39.  HIGH(Ze)  -  a  string  of  le    copies  of  the   highest  character 
in  the  collating  sequence. 

40.  IMAG(a:)  -  the  imaginary  part  of  the  complex  number  x. 

41.  INDEX  (sa,c?a)  -  the  position  within  the  string  sa   of  the 
first  occurrence  of  the  string  aa.      The  value  is  0  if  oa 
does  not  occur  within  sa. 

42.  LBOUND(x,n)  -  the  lower  bound  of  the  n-th  subscript  of 
the  array  x. 

43.  LENGTH (sa)  -  the  length  of  the  string  sa . 

44.  LINENO(/n)  -  the  current  line  number  (within  a  page)  of 
the  print  file  fn. 

45.  LOG(x)  -  the  natural  logarithm  of  x. 

46.  LOGIO (x)  -  the  logarithm  to  the  base  10  of  x.    x  must 
not  be  complex. 

47.  L0G2(x)  -  the  logarithm  to  the  base  2  of  x.   x  must  not 
be  complex. 

48.  LOW(Ze)  -  a  string  of  le    copies  of  the  lowest  character 
in  the  collating  sequence. 

49.  MAX(x  ,x„ , . . . ,x  )  -  the  largest  of   the  numerical  values 
of  the  X.  .   The  x.  must  not  be  complex. 

50.  MIN (x- ,x_, . . . ,x  )  -  the  smallest  of  the  numerical  values 

of  the  X. .   The  x.  must  not  be  complex. 
11 

51.  MOD(x,iy)  -  the  value  of  x  modulo  y.    x   and  y   must  not  be 

complex. 

56 


52.  MULTIPLY(x,  z/,p  [,(^1  )  -  the  product  of  x   and  y   with  preci- 
sion {p,q)    or  (p,0)  if  the  result  is  fixed,  and  with 
precision  (p)  if  it  is  float. 

53.  NULL{  )  -  the  null  pointer. 

54.  OFFSET  ipt ,ar)    -    the  result  of  converting  the  pointer 
pt    to  an  offset  within   the  area  ar . 

55.  ONCHAR (  )  -  the  leftmost  erroneous  character   within  the 
current  ONSOURCE -value .   When  the  conversion-condition 

is  raised,  the  current  ONSOURCE -value  is  set  to  the  string 
whose  conversion  was  being  attempted,  and  it  retains  this 
value  during  the  execution  of  the  associated  on-unit. 

56.  ONCODE (  )  -  an  implementation-defined  integer  indicating 
the  nature  of  the  on-condition  associated  with  the  current 
on-unit . 

57.  ONFIELD (  )  -  the  contents  of  an  erroneous  field   encountered 
during  data-direct  input,  causing  the  name  condition  to  be 
raised. 

58.  ONFILE(  )  -  the  name  of  the  file  being  processed  when  an 
input-output  condition  was  raised. 

59.  ONKEY  (  )  -  the  name  of  an  erroneous  key  that  caused  the 
KEY-condition  to  be  raised  during  record  input-output. 

60.  ONLOC (  )  -  the  name  of  the  procedure  entry-point  active 
when  the  current  on-unit  was  raised. 

61.  ONSOURCE (  )  -  the  current  onsource-value .   When  the  CONVER- 
SION -condition  is  raised,  the  current  ONSOURCE -value  is 

set  to  the  string  whose  conversion  was  being  attempted, 

62.  PAGENO(/>i)  -  the  number  of  the  current  page  within  the 

print  file  fn. 

57 


63.  POINTER (0/6,0^)-  the  result  of  converting  the  offset 
ofe   within  the  area  ar      to  a  pointer. 

64.  PRECISION  (x,p  [,(?] )   -  the  result  of  converting  x   to 
precision  (p/C?)  or  (p,0)  if  x    is  fixed,  and  to  precision 
(p)  if  X    is  float. 

65.  PROD(x)  -  the  product  of  all  the  elements  of  the  array  x. 

66.  RE7U1.  (x)  -  the  real  part  of  the  complex  number  x. 

67.  REVERSE (sa)  -  the  bits  or  characters  of  the  string  sa 
taken  in  reverse  order. 

68.  ROUND (x,n)  -  the  result  of  rounding  up  the  numerical  value 
of  x.   If  a;  is  fixed,  the  result  has  a  scale-factor  of  n; 
otherwise  the  result  has  a  number-of-digits  of  n. 

n   must  be  an  integer  constant. 

69.  SIGN(x)  -  the  value  +1,  0,  or  -1  according  to  whether  x   is 
positive ,  zero   or  negative . 

70.  SIN(x)  -  the  sine  of  x. 

71.  SIND(x)  -  the  sine  of  x,  with  x  given  in  degrees. 
X  must  not  be  complex. 

72.  SINH(x)  -  the  hyperbolic  sine  of  x. 

73.  SOME(x)  -  the  value  'I'B  if  at  least  one  bit  in   x  is  a 
one-bit,  and  'O'B  otherwise.   For  this  purpose,  all 
scalar-elements  of  x  are  converted  to  bit. 

74.  SQRT(x)  -  the  square  root  of  x. 

75.  STRING (sa)  -  the  result  of  concatenating  together  the 
scalar-elements  of  sa   after  converting  them  to  bit. 

76.  SUBSTR(sa, st [ , Ze] )  -  the  substring  of  sa   consisting  of  le 
characters  or  bits  of  sa   beginning  with  the   st-th  one. 

58 


Table  5.   Continued 

If  le    is  omitted,  the  substring  consists  of  the 
characters  or  bits  from  the  si-th  one  to  the  last. 

77.  SUBTRACT(x,  i/,p  [  ,(7]  )   -  the  difference  of  x    and  y   with 
precision  {^  ,q)    or  (p,0)  if  the  result  is  fixed,  and 
with  precision  (p)  if  it  is  float. 

78.  SUM(x)  -  the  sum  of  all  the  elements  of  the  array  x. 

79.  TAN(a:)  -  the  tangent  of  x. 

80.  TAND(x)  -  the  tangent  of  x,    with  x   given  in  degrees. 
X   must  not  be  complex. 

81.  TANH (x)  -  the  hyperbolic  tangent  of  x. 

82.  TIME (  )  -  the  current  time,  in  the  form  hhmmss      where 

hh  gives  the  hour,  mm  gives  the  minute,  and  ss...s  gives 
the  second  carried  to  an  implementation-defined  number  of 
fractional  decimal  places. 

83.  TRANSLATE (sa,ra [, pa] )   -  the  result  of  replacing,  within 
sa,      each  character  of  pa      by  the  corresponding  character 
of  ra .       If  pa    is  omitted,  it  is  taken  to  be  the  collating 
sequence . 

84.  TRUNC(x)  -  the  result  of  truncating  x    to  the  nearest  integer 
in  the  direction  of  zero.   x  must  not  be  complex. 

85.  UNSPEC(x)  -  the  internal  representation  of  x,  as  a  bit  string 

86.  VALID(f3a)  -  the  value  'I'B  if  the  current  value  of  the 
pictured  variable  ea   conforms  to  the  picture  and  'O'B 
otherwise . 

87.  VERIFY  (8a,r!a)  -  the  position  within  the  strin--!  •  •  of  the 

first  character  or  bit  of  SA  that  does  not      ir  within  oa . 

aa    thus  behaves  as  a  set  rather  than  a  sequence.  If  all 

characters  or  bits  of  sa    occur  within    ,  the  value  of  VERIFY 
is  0.  53 


the  string  x   that  is  not  a  character  of  the  string  y.      If  all 
the  characters  of  x   appear  in  y ,    then  the  value  of  VERIFY  is  0. 
For  example: 

value  of  VERIFY ( 'CABDRIVER' , 'ABCDE')  =  5 

value  of  VERIFY ( 'CEDE' , 'ABCDE' )  =  0 

The  builtin  function  REVERSE (x)  reverses  its  argument,  so  for 
example: 

value  of  REVERSE ( 'GALLOP')  =  ' POLLAG ' 

This  function  is  useful  in  right-to-left  scanning;  the  string 
to  be  scanned  is  reversed  and  then  scanned  left  to  right.  The 
builtin  function  COLLATE (  )  (the  "(  )"  indicates  that  COLLATE 
is  a  function  of  no  arguments)  has  as  its  value  the  implemen- 
tation-defined collating  sequence,  i.e.,  the  string  consisting 
of  all  acceptable  characters  ordered  from  least  to  greatest. 
The  builtin  function  COPY(x,n)  creates  n   copies  of  the  string  x. 
For  example, 

value  of  COPY( 'CHA' , 3)  =  'CHACHACHA' 

The  arithmetic  builtin  functions  enable  the  user  to  control 
the  attributes  of  arithmetic  results.   These  fall  into  two 
groups.   First,  there  are  builtin  functions  ADD,  SUBTRACT, 
MULTIPLY,  and  DIVIDE  that  behave  like  the  corresponding  infix 
operators,  except  that  the  precision  of  the  result  is  explicitly 
specified.   For  example, 

MULTIPLY (Ml , M2 , 5 , 3 ) 

produces  a  result  whose  precision  is  (5,3),  and  whose  remaining 
attributes  are  determined  by  the  attributes  of  Ml  and  M2 . 

60 


Second,  there  are  builtin  functions  FIXED,  FLOAT,  BINARY,  and 
DECIMAL  that  convert  their  argument  to  the  specified  attribute 
with    the     specified  precision.   For  instance, 

DECIMAL(M1,4,2) 

converts  Ml  to  fixed  decimal  with  precision  (4,2) ;  the  mode  of 
the  result  is  the  mode  of  Ml. 

The  conversion  builtin  functions  FIXED,  FLOAT,  BINARY,  and 
DECIMAL  can  be  used  not  only  to  convert  among  arithmetic  types 
but  also  to  convert  from  the  string  types.  The  rules  for  the 
conversion  are  discussed  under  "Type  Conversion"  below.  There 
are  further  conversion  functions  REAL(j-)  which  converts  x    to 
real  type  (and  for  a  complex  number,  takes  its  real  part) ; 
IMAG(x),   which  takes  the  imaginary  part  of  the  complex  number 
X    (and  yields  0  if  x  is  not  a  complex  number)  ;  and  COMPLEX  (x,  z/ )  , 
which  converts  x   and  z/  to  a  common  real  type  and  then  forms 
the  complex  number  x   +    ii/ .      For  conversion  to  string  types,  the 
builtin  functions  CHARACTER  (x,  n)   and  BIT(a:,n)  can  be  used. 
CHARACTER ( X, ^)  first  converts  x  to  character  type  and  then 
adjusts  the  length  of  the  result  to  n  either  by  truncating  on 
the  right  or  by  filling  on  the  right  with  blanks.   BIT  behaves 
similarly,  either  truncating  or  filling  with  zero-bits.  The 
second  argument  of  either  of  these  functions  may  be  omitted, 
in  which  case  no  truncation  or  filling  is  done. 

The  mathematical  builtin  functions  included  in  PL/I  are 
listed  in  Table  6.   With  the  exceptions  indicated,  they  can 
accept  arguments  of  any  arithmetic  type,  including  c-      x 
types.   For  some  mathematical  functions  there       e   than  one 

61 


possible  range  for  the  result  value,  and  the  choice  of  principal 
value  is  specified  in  the  table. 

The  function  ATAN  (arctangent)  can  accept  either  one   or 
two  arguments.   The  two-argument  version  is  useful  in  converting 
rectangular  coordinates  to  polar  coordinates.  If  the  rectangular 
coordinates  are  given  by  the  pair  {x,y),    then  ATAN{x,y)    gives 
the  corresponding  polar  angle  in  the  range  from  -it  to  +tt  .  Since 
the  value  of  the  tangent  function  repeats  every  tt/2  radians, 
the  sign  of  y    is  needed  to  determine  the  correct  value. 

A  number  of  the  builtin  functions  fall  into  no  particular 
category.   The  builtin  function  SUM(j:)  accepts  an  array  as 
argument,  and  returns  as  value  the  sum  of  all  the  elements  of 
the  array.   The  builtin  function  PROD,  for  "product",  behaves 
similarly.   The  builtin  function  DOT[x,y)       expects  its  arguments 
to  be  one-dimensional  arrays  both  having   the  same  bounds; 
it  takes  the  mathematical  dot  product  of  x   and  y.      The  builtin 
function  BOOLix ,y , z)       takes  as  arguments  two  bit  strings  x   and 
y   of  arbitrary  length,  and  a  third  bit  string  z    of  length  4. 
z   determines  a  boolean  function  that  is  applied  to  x   and  y . 
If  z    is  the  sequence  b^b^b ^b  .       ,    then  the  function  is  defined 


by; 


bit  of  X  bit  of  y        result 
0  0        2?^ 

0  1        ^2 

1  0        2»3 
1  lb. 


62 


Table  6.   Mathematical  Builtin   Functions 


PL/I         Mathematical 
Name        Description 


ABS  absolute  value 

ACON  arc  cosine 

ASIN  arc  sine 

ATAN  arc  tangent  (one  argument) 

ATANp  arc  tangent  of  quotient 
(two  arguments) 

ATAND,  arc  tangent  in  degrees 
(one  argument) 

ATAND-  arc  tangent  of  quotient 

in  degrees  (2  arguments) 

ATANH  hyperbolic  arctangent 

COS  cosine 

COSD  cosine  in  degrees 

COSH  hyperbolic  cosine 

ERF  error  function 

ERFC  complement  of  error  func- 
tion 

EXP  exponential 

LOG  natural  logarithm 

L0G2  base  2  logarithm 

LOGIO  base  10  logarithm 

SIN  sine 

SIND  sine  in  degrees 

SINH  hyperbolic  sine 

SQRT  square  root 

TAN  tangent 

TAND  tangent  in  degrees 

TANH  hyperbolic  tangent 


Complex     Constraints  on 
Arguments?   Result  R 

(Principal  Value) 


yes 

R    5^  0 

no 

0       <    R       <    TT 

no 

-tt/2      <   R      <   TT/2 

yes 

-7T/2     <  R     <  tt/2 

no 
no 

yes 

yes 

no 

yes 

no 

no 

yes 

yes 

no 

no 

yes 

no 

yes 

yes 

yes 

no 

yes 


(real  argument) 

-TT   <  Re  (R)    <  TT 

(complex  argument) 
-90  <  R  <  90 

-180  <  R  <  180 


-TT   <  Im(R)   <  TT 


Re(R)  >  0   or 
Re(R)=0  and  Im(R) >0 


63 


The  builtin  function  VALID  can  be  used  to  check  the 
validity  of  pictured  data,  i.e.,  to  ensure  that  the  value 
stored  in  a  pictured  variable  fits  the  description  given  by 
the  picture.   VALID  (j:)  returns  'I'B  if  the  pictured  variable 
X   contains  a  valid  value,  and  'O'B  otherwise.   Invalid  values 
can  arise  since  an  arbitrary  character  string  can  be  read 
into  or  assigned  to  a  pictured  varible,  and  ordinarily  no 
validity  check   is  made  at  the  time  of  reading  or  assignment. 

The  builtin  functions   EVERY  and  SOME  are  useful  in 
testing  properties  of  aggregates.   EVERY (x)  returns  'I'B  if 
its  argument  (after  conversion  to  bit  type,  if  necessary) 
consists  entirely  of  one-bits,  and  'O'B  otherwise.   SOME (x) , 
on  the  other  hand,  returns  'I'B  if  its  argument  contains  at 
least  one  one-bit,  and  'O'B  otherwise.   For  example,  if  A  is 
an  array  of  arithmetic  type,  then  the  expression  A  >  0  will 
be  an  array  with  a  one-bit  in  each  position  i   where  A{i)     >  0. 
Therefore  EVERY (A>0)  will  return  'I'B  if  all  elements  of  A 
are  greater  than  0,  while  SOME(A>0)  will  return  'I'B  if  at 
least  one  element  of  A  is  greater  than  0. 

Type  Conversion 

In  PL/I  it  is  possible  to  convert  from  any  printable 
type  to  any  other,  although  for  certain  values  the  conversion 
may  be  illegal.   Conversions  may  be  invoked  either  explicitly, 
using  builtin  functions  such  as  FLOAT  or  CHARACTER,  or 
implicitly  in  contexts  such  as  operands  of  operators  or 
arguments  of  functions.   For  instance,  the  concatenation 

64 


operator  requires  that  its  operands  be  strings  of  the  same  type 
(bit  or  character) ,  so  that  the  operands  must  be  converted 
appropriately  —  even  if  they  are  of  arithmetic  type.  The 
text  of  a  procedure  defines  the  types  of  its  parameters,  and 
if  the  arguments  of  a  procedure  do  not  already  have  the  expect- 
ed types,  they  too  must  be  converted.   In  fact,  PL/I  provides 
implicit  conversions  in  almost  every  context  where  conversion 
is  possible. 

The  conversions  among  arithmetic  types  generally  follow 
the  principle  of   preserving  the  meaning  of  the  converted 
value.   For  example,  the  result  of  converting  the  fixed  value 
7.3  to  complex  float  decimal  with  precision  (8)  is 
. 73000000E+01+0I .   In  conversion  to  a  fixed  type  when  digits 
must  be  dropped,  the  result  value  is    obtained  by  truncating 
towards  0,  although  in  certain  unusual  cases  an  implementation 
may  produce  a  slightly  different  result.   When  converting 
from  real  to  complex   an  imaginary  part  of  0  is  added,  while 
when  converting  from  complex  to  real  the  imaginary  part  is 
dropped . 

The  conversion  between  bit  and  character  is  straightfor- 
ward; zero-bits  correspond  to  the  character  "0",  and  one-bits 
correspond  to  the  character  "1".   A  character  string  to  be 
converted  to  bit  type  must  consist  entirely  of  these  two 
characters,  or  an  error  is  signalled  —  specifically,  the 
CONVERSlON-condition .   It  is  possible  for  the  programmer  to 
modify  the  converted  value  so  as  to  correct  the  error  (see 
"Categorization  of  the  ON-Conditions"  below) . 

65 


The  most  complicated  conversions  are  those  between  the 
string  types  and  the  arithmetic  types.   A  character  string  is 
converted  to  a  number  by  treating  the  string  as  the  represen- 
tation of  a  number.    Thus,  given  the  statements 

DECLARE  NUMV  FIXED  DECIMAL (5 , 2 ) ; 
NUMV  =  ']6]62.13E1]6'  ; 

the  string  ']6]62 .13E1\6'    is  converted  first  to  the  float  value 
that  it  represents,  and  then  to  the  fixed  decimal  value  21.30. 
The  blanks  surrounding  the  number  are  always  permissible. 
An  all-blank  value  converts  to  zero.   If  the  character  string 
does  not  represent  a  valid  number,  then  the  CONVERSION-condi- 
tion  is  signalled.   As  in  the  case  of  conversion  from  character 
to  bit,  it  is  possible  for  the  programmer  to  correct  the  error. 

Conversion  from  a  number  to  a  character  string  yields, 
in  effect,  the  result  of  printing  the  number.   Ordinarily  that 
result  includes  leading  blanks.   For  instance,  the  effect  of 

DECLARE  NUMV  FIXED  DECIMAL (4); 

DECLARE  CONV_RESULT  CHARACTER (20 )  VARYING; 

NUMV  =  17; 

CONV_RESULT  =  NUMV; 

is  to  assign  the  string  ']^)6]6]6]611 '    to  NUMV.   In  most  cases  the 
length  of  the  resulting  string  is  the  number-of-digits  after 
conversion  (if  necessary)  to  fixed  decimal,   plus  three. 
Three  spare  positions  are  needed  in  order  to  accommodate  a 
possible  sign,  a  possible  decimal  point,  and  a  possible  leading 
zero. 


66 


Conversion  from  an  arithmetic  value  to  a  bit  string  is 
accomplished  by  first  converting  the  arithmetic  value  to  real 
fixed  binary  and  then  converting  the  integer  part  of  the 
value  to  the  corresponding  bit   string.   For  instance,  convert- 
ing the  value  12.6  to  a  bit  string  yields  'OOllOO'B,  with  an 
intermediate  conversion  from  fixed  decimal  with  precision 
(3,1)  to  fixed  binary  with  precision  (10,4),   (The  rules  for 
obtaining  the  intermediate  precision  are  somewhat  complicated, 
but  it  can  be  seen  that  two  digits  to  the  left  of  the  decimal 
point  may   require  as  many  as  six  nonzero  bits  to  represent 
their  value.)   Conversion  from  a  bit  string  to  an  arithmetic 
type  is  accomplished  by  treating  the  bit  string  as  a  binary 
number,  and  then  converting  from  that  number  to  the  desired 
type. 

It  is  also  possible  to   convert  from  pointer  to  offset, 
or  vice  versa,  provided  that  an  area  is  given.   Thus  if  AR 
is  an  area  and  P  is  a  pointer,  the  expression  OFFSET(P,AR) 
gives  the  result  of  converting  P  to  an  offset  relative  to  A. 
Similarly,  if  OFS  is  an  offset,  POINTER (OFS , A)  gives  the 
result  of  converting  OFS  to  a  pointer  relative  to  A.  A  pointer 
can  be  declared  with  an  area-reference,  as  in 

DECLARE  AR2  AREA; 
DECLARE  02  OFFSET ( AR2); 

In  this  case,  02  can  implicitly  be  converted  to  a  pointer, 
and  the  .;:^.i  AR2  is  used  in  the  conversion. 


67 


Promotion 

The  PL/I  operators,  and  many  of  the  builtin  functions 
also,  can  be  applied  to  aggregates  as  well  as  to  scalars.  When 
two  aggregates  of  the  same  organization,  i.e.,  two  structures 
with  equal  numbers  of  components  or  two  arrays  of  the  same 
dimensionality,  are  used  as  the  operands  of  an  operator,  the 
result  also  has  that  organization,  and  the  result  is  formed 
by  combining  corresponding  components  of  the  operands. 
For  example,  in 

DECLARE  A (3, 4)  FIXED  BINARY 

DECLARE  B{3,4)  FIXED  BINARY 

DECLARE  C(3,4)  FIXED  BINARY 

A  =  B  +  C; 

the  assignment  to  A  is  accomplished  by  adding  B(l,l)  to  C{1,1), 
B(l,2)  to  C(l,2),  etc.,  to  form  a  new  array  of  sums  with 
dimensionality  (3,4).   The  array  of  sums  is  then  copied  into  A. 
In  certain  peculiar  cases  the  temporary  array  containing  the 
sura  is  actually  needed,  and  it  does  not  suffice  simply  to  add 
the  elements  of  B  and  C  one  by  one  and  place  the  result  directly 
in  A. 

It  is   also  possible  to  combine  scalars  with  structures, 
scalars  with  arrays,  and  structures  with  arrays.   However,  it 
is  not  possible  to  combine  structures  having  different  numbers 
of  members,  or  arrays  having  different  dimensionalities.  A 
scalar  is  combined  with  a  structure  by  promoting  it  to  a 
similar  structure,  all  of  whose  members  have  the  same  value  as 
the  scalar.   Similarly,  a  scalar  is  combined  with  an  array  by 

68 


promoting  the  scalar  to  a  similar  array.   The  case  of  combin- 
ing a  structure  with  an  array  is  more  complicated;  first  the 
original  structure  is  promoted  to  an  array  of  structures,  and 
later  each  element  of  the  original  array  is  promoted  to  a 
structure.   A  simple  instance  of  promotion  is   given  by  the 
expression  A+1,  where  A  is  an  array  of  arithmetic  type.  The 
value  of  this  expression  is  obtained  by  creating  an  array  of 
I's,  having  the  same  dimensionality  as  A,  and  then  adding 
this  new  array  to  A,  element  by  element.   The  effect  is  just 
the  same  as  adding  1  to  each  element  of   A. 

The  Assignment-Statement 

The  assignment-statement  contains  a  left  side,  which  is 
a  list  of  targets,    and  a  right  side,  which  is  an    expression. 
Each  of  the  targets  designates  a  location  capable  of  receiving 
a  value.   The  statement  is  executed  by  evaluating  the  expres- 
sion and  then  storing  its  value,  after  appropriate  conversion, 
into  the  location  designated  by  each  target,  in  order  from 
left  to  right.   For  instance,  the  assignment-statement 

A,  B(I) ,  C=l; 
causes  1  to  be  stored  into  A,  B(I),  and  C.    The  targets  of 
an  assignment-statement  may  be  variables  or  pseudo-variables. 
For  instance,  the  assignment-statement 

SUBSTR(TEXT,I,LEN)  =  WORD; 

stores  the  value  of  WORD  into  the  indicated  substring  of  TEXT 
(after  adjusting  the  size  of    the  value  to  be  LEN) .  Similarly, 

69 


IMAG(Z)  =  SIN(X)  ; 

causes  the  imaginary  part  of  the   (necessarily)  complex  vari- 
able Z  to  be  set  to  the  value  of  SIN(X) ,  while  the  real  part 
of  Z  is  left  undisturbed. 

Since  the  type  of  the  value  obtained  from  the  right  side 
of  an  assignment-statement  may    disagree  with  the  type  of  a 
target,  promotion  or  conversion,  or  both,  may  be  necessary. 
If  the  target  is  a  scalar,  then  the  usual  rules  for  scalar 
conversion  are  applied;  the  type  of  the  target  defines  the 
type  to  which  the  value  must  be  converted.   If  the  target  is 
a  structure  or  array,  then  the  value   must  be  promoted  to  the 
type  of  that  structure  or  array,  by  replicating  elements  as 
necessary.   Following  the  promotion,  element-by-element  scalar 
conversion  may  be  necessary.  For  instance,  in  the  example 

DECLARE  HVAROO)  FLOAT  BINARY; 
HVAR  =0; 

the  scalar  value  0  is  promoted  to  an  array  of  30  fixed  zeros. 

Each  of  these  is  then  converted  to  an  appropriate  float  zero, 

* 
and  assigned  to  the  corresponding  element  of  the  array. 

A  variation  on  the  assignment-statement,  called 

by-name    assignment ,    can  be  used  to  assign  elements  from  one 

structure  to  another  according  to  the  names  of  the  elements 

rather  than  according  to  their  positions  in  the  structure. 

For  instance,  given  the  declarations 


*   In  actual  practice,  the    conversion  is  usually  done  before 
rather  than  after  the  promotion.   The  result  is  the  same. 


70 


DECLARE 

1   RED, 

2   BLUE, 
2   GREEN, 

3   ORANGE, 
3   WHITE, 
2   BLACK , 
2   GRAY ; 
DECLARE 

1   VIOLET, 
2   BLACK , 
2   WHITE, 
2   GREEN, 

3   ORANGE , 
3   VIOLET, 
2   TAN 
2   BLUE; 

the  effect  of  the  assignment-statement 

RED  =  VIOLET,  BY  NAME ; 

is  to  perform  theindividual  assignments 

RED. BLUE  =  VIOLET, BLUE; 

RED. GREEN. ORANGE  =  VIOLET . GREEN .ORANGE; 

RED. BLACK  =  VIOLET .BLACK ; 

Those  members  not  in  common  between  the  two  structures  are 
ignored.   By-name  assignment  can  be  extended  to  accommodate 
expressions  that  involve  structures. 


71 


STORAGE  TYPES 

PL/I  provides  a  variety  of  ways  to  manage  the  storage 
of  variables.   Each  variable  has  a  storage    type,    which  can 
be  either  parameter ,    defined,    or  a  storage  class.   The  parameter 
and  defined  storage  types    indicate  that  the  variable  is  an 
alias,  i.e.,  an  alternate  name,  for  storage  that  has  already 
been  obtained  by  other  means.   The  storage    classes   provide 
different  ways  of  allocating   and  freeing  storage;  the  storage 
classes  are  static,    automatic ,    controlled ,    and  based.       The 
storage  type  of  a  variable  is  determined  by  its  declaration 
after  all  defaulting  of  declarations  has  been  done;  in  most 
cases  the  default  is  the   automatic  type.  The  storage  used 
to  hold  the  value  of  a  variable  is  called  a  generation .    A 
generation  can  exist  even  though  it  is  not  currently  associated 
with  any  variable. 

Static  Storage 

The  static  storage  class  is  the  simplest  one.   When  a 
variable  is  declared  to  be  static,  its  generation  is  allocated 
at  the  start  of  program  execution  and  remains  allocated 
throughout  program  execution.   When  a  static  variable  is 
declared  within  a  procedure,  the  values  of  that  variable  are 
kept  from  one  call  of  the  procedure  to  the  next.   Even  in  the 
case  of  a  recursive  procedure,  there  is  just  one  copy  of  the 
variable,  and  that    copy  is  available  at  all  levels  of  recur- 
sion.  Static  storage  is  much  like  the  standard  form  of  storage 
in  FORTRAN. 

72 


Automatic  Storage 

Storage  for  an  automatic  variable  is  allocated  on  entrance 
to  the  block  where  the  variable  is  declared,  and  freed  on  exit 
from  that  block.   Whenever  the  block  is  entered,  a  fresh  gener- 
ation is  obtained  for  the  variable.   In  practice  it  sometimes 
happens  that  values  of  automatic  variables  are  retained  from 
one  block  entrance  to  the  next,  but  this  behavior  is  not  any- 
thing that  the  programmer  can   rely  upon.   When  an  automatic 
variable  is  declared  within  a  recursive  procedure,  a  new 
generation  is  created  for  each  level  of  recursion,  and  remains 
associated  with  the  variable  at  that  recursion  level  until 
the  recursion  level  is  terminated.  Automatic  storage  resembles 
the  ordinary  local  storage  of  Algol . 

Controlled  Storage 

Controlled  storage  is  explicitly  allocated  and  freed  by 
the  programmer  using  the  ALLOCATE-statement  and  the  FREE-state- 
ment.   Each  time  the  variable  is  allocated,  a  new  generation 
for  it  is  created  and  placed  on  a  pushdown  stack;  each  time  the 
variable  is  freed,  the  generation  at  the  top  of  the  stack  is 
destroyed.   There  is  one  such  stack  for  each  controlled  variable, 
and  the  current  value  of  the  variable  is  always  obtained  from 
the  generation  at  the  top  of  the  stack.   In  other  words,  the 
generations  follow  a  last-in-first-out  rule. 

The  values  of  string  lengths  and  array  dimensions  in  the 
declaration  of  a  controlled  variable  can  be  given  by  expressions. 

73 


The  expressions  are  evaluated  when  a  new  generation  is  allocated, 
and  so  the  different  generations  need  not  all  have  the  same 
sizes.   For  example,  suppose  that  we  are  given  the  statements: 

DECLARE  N  FIXED; 

DECLARE  CONTV  CHARACTER (N)  CONTROLLED; 

N  =  5; 

ALLOCATE  CONTV; 

CONTV  =  'FIRST' ; 

N  =  7; 

ALLOCATE  CONTV; 

CONTV  =  'SECOND' ; 

If  CONTV  has  not  been  previuosly  allocated,  these  statements 
will  create  a  stack  consisting  of  two  generations.  The  genera- 
tion at  the  top  of  the  stack  will  have  length  7  and  value 
'SECONDjzJ'  (the  assignment  adds  a  blank  on  the  right)  ,  while 
the  other  generation  will  have  length  5  and  value  'FIRST'. 
Thus  the  current  value  of  CONTV  will  be  'SECONDfc^'.  If  the 
statements 

FREE  CONTV; 

PUT  LIST (CONTV) ; 

are  executed,  then  the  generation  at  the  top  of  the  stack  will 
be  destroyed  and  CONTV  will  refer  to  the  first  generation. 
Consequently  the  PUT-statement  will  cause  FIRST  to  be  printed. 

Based  Storage 

Based  variables  are  useful  in  creating  linked  data  struc- 
tures, and  also  have  applications  in  record  input-output.  A 
based  variable  does  not  have  any  storage  of  its  own;  instead, 
the  declaration  acts  as  a  template  and  describes  a  generation 
of  storage.   In  order  to  use  the  variable  to  refer  to  a 

74 


particular  generation  of  storage,  a  pointer  to  that  generation 
must  also  be  provided.   The  pointer  and  the  based  variable, 
taken  together,  constitute  a  based  reference .       In  many  cases, 
the  pointer  is  given  implicitly  rather  than  explicitly. 

An  example  of  a  declaration  of  a  based  variable  and  a 
pointer  is 

DECLARE 

1   ARRAY_ELT  BASED, 

2   ARRAY (10)  FLOAT, 

2   NEXT_ELT  POINTER; 
DECLARE  AP  POINTER; 

ARRAY_ELT  describes  a  generation  of  storage,  namely,  a  struc- 
ture  containing  a  float  array  and  a  pointer.  The  based 
reference 

A  - >  ARRAY ( 4 ) 

designates  a  particular  element  within  the  ARRAY_ELT  structure 
pointed  at  by  the  pointer  AP ,  and  if  AP  does  not  point  at  such 
a  structure  the  reference  is  invalid.   PL/I  does  not  provide 
any  mechanism  for  checking  that  a  pointer  is  indeed  pointing 
at  a  generation  of  the  correct  type,  and  so  it  is  entirely  the 
programmer's  responsibility.   The  errors  that  result  when  a 
pointer  points  at  an  object  of  the  wrong  type  can  often  be 
extremely  difficult  to  track  down. 
The  statement 

ALLOCATE  ARRAY_ELT  SET(AP); 

causes  a  generation  matching  the  type  of  ARRAy_ELT  to  be 
created  and  also  causes  the  pointer  AP  to  point  at  that  genera- 
tion.  Thus,  after  this  ALLOCATE-statement  has  been  executed, 

75 


a  reference  to  AP  ->  ARFIAY(4)  will  be  valid.   If  subsequently 
the  statement 

FREE  AP  ->  ARRAY_ELT; 

is  executed  (and  the  value  of  AP  has  not  been  changed  in  the 
meantime) ,  the   generation  pointed  at  by  AP  will  be  destroyed, 
and  subsequent  references  to  that  generation  will  be   meaning- 
less . 

In  this  example,  the  structure  includes  not  only  the 
array  but  also  a  pointer.   That  pointer  can  be  used  to  form 
a  list  of  arrays,  each  one  pointing  to  its  successor.  An 
element  is   added  to  the  head  of  the  list  by  allocating  it 
and  setting  its  NEXT_ELT  component  to  the  previous  list  head. 
Similarly,  the  head  of  the  list  is  deleted  by  setting  the 
new  list  head  to  the  NEXT_ELT  component  of  the  old  list  head 
and  then  freeing  the   old  list  head.   One  of  the  main  uses 
of  based  variables  and  pointers  in  PL/I  is  constructing  lists 
such  as  this  one.   In  order  to  end  a  list,  a  special  null 
pointer  is  needed,  and  that  pointer  is  provided  by  the  NULL 
builtin  function. 

It  is  convenient  to  have  to  write  a  pointer  with  every 
based  reference.   Therefore  it  is  possible  to  declare  an 
implicit  pointer  in  the  declaration  of  a  based  variable,  e.g., 

DECLARE  BFIX  BASED (BFP)  FIXED; 
DECLARE  BFP  POINTER; 

A  reference  to  BFIX  by  itself  is  taken  to  mean  BFP  ->  BFIX. 

Moreover,  the  statement 

76 


ALLOCATE  BFIX; 
is  equivalent  to 

ALLOCATE  BFIX  SET(BFP); 
and  the  statement 

FREE  BFIX; 
is  equivalent  to 

FREE  BFP  ->  BFIX; 

The  template  given  by  a  based  variable  can  be  applied  to 
storage  of  types  other  than  based.   In  order  to  obtain  a  pointer 
to  a  generation,  the  ADDR  builtin  function  is  used.   ADDR(y) 
yields  a  pointer  to   the  generation  specified  by  y .    As  an 
example,  the  statements 

DECLARE  BCOMP  FLOAT  COMPLEX  BASED; 
DECLARE  SCOMP  FLOAT  COMPLEX  STATIC; 
ADDR(SCOMP)  ->  BCOMP  =  2E0  +  3E0I; 

cause  the  static  variable  SCOMP  to  be  set  to  the  value  2E0+3E0I. 

The  Refer-Option 

The  string  lengths  and  array  bounds  of  a  based  variable 
can  be  specified  by  expressions  as  well  as  by  constants.  For 
example,  the  declaration 

DECLARE  ECS  CHARACTER (M)  BASED; 

indicates  that  the  length  of  BCS  is  given  by  the  current  value 
of  M.   When  BCS  is  allocated,  the  generation  that  is  created 
will  have  a  length  given  by  the  current  value  of  M,  and  when 
reference  is  made  to  BCS,  the  value  of  M  must  agree  with  the 

77 


length  of  the  string  in  the  generation  referred  to.   If  a 
number  of  generations,  all  corresponding  to  BCS ,  exist,  it 
may  be  difficult  to  ensure  that  the  current  value  of  M  is 
correct,  since  the  generations  may  have  different  string 
lengths.   In  order  to  deal  with  this  difficulty,  PL/I  allows 
the  string  length  to  be  specified  along  with  the  string 
itself;  both  the  string  and  the  length  are  stored  in  a  single 
structure,  sometimes  called  a  set f- defining    structure.   For 
instance,  the  structure 

DECLARE 

1   STRING_STRUC  BASED (STP), 

2   LEN  FIXED, 

2   NEXT  POINTER, 

2   STRING  CHARACTER ( LEN 1  REFER(LEN)); 
DECLARE  STP  POINTER; 

could  be  used  to  create  a  list  of  strings,  each  having  a 
different  length.   When  one  of  these  structures  is  allocated, 
the  length  of  the  string  is  obtained  as  the  current  value  of 
LENl,  and  at  the  same  time  the  current  value  of  LENl  is  auto- 
matically stored  within  the  LEN  component  of  the  newly  created 
generation.   When  one  of  these  structures  is  referenced,  the 
length  of  STRING  is  obtained  from  the  LEN  component  of  that 
structure.   Both  LEN  and  LENl  are  needed,  for  the  following 
reason.   Were  LEN  used  without  the  so-called  refer-option, 
the  allocation  size  would  be  taken  from  STP->LEN  prior  to  the 
allocation,  which  would  be  either  undefined  or  the  string 
length  of  a  previously  allocated  generation.   On  the  other 
hand,  were  LENl  used,  it  would  then  be  necessary  to  reset  it 
to  LEN  before  referencing  STRING,  since  otherwise  the  length 

78 


of  STRING  would  not  be  correct. 

A  based  variable  may  contain  any  number  of  ref er-options . 
These  can  be  used  to  specify  upper  or  lower  array  bounds  as 
well  as  string  lengths  and  area  sizes. 

Lef t-to-Right  Correspondence 

It  is  often  necessary  to  create  data  structures  in  which 
the  elements  do  not  all  have  the  same  type,  as  in  the  following 
example: 

DECLARE 

1  FLOAT_ELEMENT  BASED (ELPTR) , 

2  ELTYPE  FIXED,    /*  1  FOR  FLOAT  */ 
2  NEXT  POINTER, 
2  VALUE  FLOAT; 

DECLARE 

1  FIXED_ELEMENT  BASED (ELPTR) , 

2  ELTYPE  FIXED,    /*  2  FOR  FIXED  */ 
2  NEXT  POINTER, 
2  VALUE  FIXED; 

DECLARE 

1  CHAR_ELEMENT  BASED (ELPTR) , 

2  ELTYPE  FIXED,    /*  3  FOR  CHARACTER  */ 

2  NEXT  POINTER, 

2  VALUE  CHARACTER (24 ) ; 

DECLARE  ELPTR  POINTER; 

A  list  can  be  formed  containing  elements  of  all  three  kinds, 
storing  a  type  code  in  ELTYPE  in  order  to  distinguish  among 
them.   In  order  to  reference  an  element,  it  is  necessary  to 
specify  either  FLOAT_ELEMENT,  FIXEDELEMENT,  or  CHAR_ELEMENT 
even  before  the  type  of  the  element  is  known,  since  a  refer- 
ence to  ELTYPE  by  itself  is  syntactically  ambiguous.  Therefore 
under  certain  conditions  PL/I  allows  a  reference  to  a  component 

79 


of  a  based  structure  even  when  the  variable  in  the  reference 
does  not  agree  with  the  generation  being  referenced.   The 
primary  condition  is  that  the  generation  and  the  variable  must 
agree  up  to  that  component,  although  there  are  further  detailed 
requirements  that  are  beyond  the  scope  of  this  article.  Based 
references  satisfying  this  constraint  are  said  to  be  in 
left-to-right    aorrespondence ,    since  they  agree  reading  from 
left  to  right.   Thus  it  is    permissible  to  use 
FLOAT_ELEMENT.ELTYPE  to  refer  to,  and  therefore  to  test,  the 
type  code  stored  in  any  one  of  the  three  kinds  of  elements. 
Even  if   ELPTR  is  pointing  at  a  generation  having  the  type  of 
CHAR_ELEMENT,  the  ELTYPE  component  of  that  generation  can  be 
referenced  using  FLOAT_ELEMENT. ELTYPE.   Since  ELTYPE  is  the 
first  component  of  each  element,  the    elements  necessarily 
agree  up  to  that  component.   Moreover,  the  NEXT  components  of 
the  three  kinds  of  elements  can  be  referenced  interchangeably 
since  in  each  kind  of  element  NEXT  has  type  pointer  and  is 
preceded  by  an  element  having  type  fixed  (with  the  remaining 
attributes  defaulted  identically  in  all  cases) . 

Allocation  in  Areas 

A  based  variable  can  be  allocated  in  a  specified  area, 
as  in  the  following  example: 

DECLARE  BV  FIXED  BASED (P); 
DECLARE  A  AREA(20  0); 
ALLOCATE  BV  IN (A) ; 

Since  BV  has  been  allocated  in  A,  the  OFFSET  builtin  function 

80 


can  be  used  to  convert  P  into  an  offset  relative  to  A,  as 
given  by 

OFFSET(P,A) 

The  allocation  can  assign  a  value  directly  to  an  offset 
variable,  as  in  the  example 

DECLARE  OFS  OFFSET (A) ; 
DECLARE  BVl  BASED (OFS); 
ALLOCATE  BVl  IN (A) ; 

Since  BVl  is  based  on  OFS,  the  offset  of  BVl  relative  to  A  is 
assigned  to  OFS  when  the  ALLOCATE-statement  is  executed. 

Parameter  Storage 

A  variable  acquires  the  parameter  storage  type  by  virtue 
of  its  appearance  in  a  parameter  list  of  either  a  PROCEDURE- 
statement  or  an  ENTRY-statement .   The  PARAMETER  attribute  can, 
but  need  not,  be  declared  for  a  parameter;  it  is  invalid  to 
use  that  attribute  for  any  other  kind  of  variable.  A  parameter 
describes  a  generation  of  storage  passed  as  an  argument  to  the 
procedure   that  declares  the  parameter.  Thus,  allocation  and 
freeing  of  the  parameter  is  the  responsibility  of  the  procedure's 
caller.  Since  a  parameter  is  allocated  before  the  procedure 
declaring  it  is  entered,  the  procedure    itself  cannot  specify 
an  initial  value  for  the  parameter.   See  "Arguments  and 
Parameters"  below  for  further  information  about  parameters. 


81 


Defined  Storage 

The  defined  storage  type,  like  the    parameter  storage 
type,  is  an  alias.   The  declaration  of  a  defined  variable 
specifies  a  base    item,    which  is  a  portion  (or  possibly  all) 
of  some  other  variable.   The  defined  variable  provides  another 
way  of  referencing  part  or  all  of  the  storage  occupied  by  the 
base  item.  The  base  item  can  be  part  of  a  variable  having  any 
storage  type  other  than  defined  or  based,  and  so  circular 
defining  is  excluded. 

There  are  three  kinds  of  defining:    simple-defining , 
isub-de fining,    and  overlay-defining .      The  sort  of  defining  that 
is  in  effect  is  determined  by  the  relation  between  the  defined 
variable  and  the  base  variable.   Since  defined  variables  are 
aliases,  they  are  not  allocated  nor  freed,  nor  are  initial 
values  specified  for  them. 

An  example  of  simple-defining  is 

DECLARE  A (5, 8)  FIXED; 

DECLARE  ADEF(2:4)  DEFINED (A (1 ,*)) ; 

ADEF  is  defined  to  consist  of  the  elements  A(l,2),  A(l,3),  and 
A(l,4).   For  simple-defining  to  be  in  effect,  the  attributes 
of  the  defined  variable  must  agree  with   those  of  the  base  item, 
except  that  the  array  bounds  of  the  defined  variable  may  be  more 
restrictive  than  the  corresponding  bounds  of  the  base  item.  A 
major  use  of  simple-defining  is  to  specify  portions  of  arrays 
that  are  to  be  passed  as  arguments  to  procedures. 


82 


Isub-def ining  is  in  effect  when  the  base  item  contains 
special  subscripts,  known  as  isubs.      These  subscripts  have 
the  form  ISUB,  2SUB,  etc.   An  example  of  isub-def ining  is 

DECLARE  A (10, 10)  FIXED; 

DECLARE  ADEF(9,8)  DEFINED (A (lSUB+1 , 2SUB+2) ) ; 

A  reference  to  an  element  of  ADEF  is  translated  into  a  refer- 
ence to  an  element  of  A  by  substituting  the  first  subscript 
for  ISUB  and  the  second  subscript  for  2SUB.    For  instance, 
ADEF(8,6)  refers  to  A(9,8).   The  defined  array  need  not  have 
the  same  dimensionality  as  the  base  item.   For  example,  in 

DECLARE  B{30,30)  FLOAT; 

DECLARE  BDIAGOO)  DEFINED  (B  ( ISUB,  ISUB)  )  ; 

the  one-dimensional  array  BDIAG  consists  of  the  diagonal 
elements  of  the  array  B,  while  in 

DECLARE  C(15)  POINTER; 

DECLARE  C2(5,3)  DEFINED (C { 3* ( lSUB-1) +2SUB) ) ; 

the  two-dimensional  array  C2  is  defined  onto  the  one-dimensional 
array  C. 

Overlay-defining  is  used  in  order  to  apply   different 
descriptions  to  strings.   For  the  purposes  of  overlay-defining, 
character  data  and  pictures  are  together  considered  as 
aharaater-alass   data,  while  bit  strings  are  considered  as 
bit-claee   data.   An  excimple  of  overlay-defining  is: 


83 


DECLARE  CS  CHARACTER ( 30 ) ; 

DECLARE  0DEF1{3)  CHARACTER (5)  POSITION (10)  DEFINED(CS); 

DECLARE 

1   0DEF2  DEFINED (CS) , 

2   OCSl  CHARACTER (14 ) , 

2   0CS2  PICTURE  '$$$V.$$';   /*  6  CHARACTER  POSITIONS*/ 


The  relationship  between  CS  and  ODEFl,  and  between  CS  and  0DEF2, 
is  illustrated  in  Figure  3.   The  POSITION  attribute  in  the 
declaration  of  ODEFl  indicates  that  the  character  sequence 
comprising  ODEFl  starts  at  character  10  of  CS .   ODEF(l) 
consists  of  characters  10-14  of  CS ,  ODEFl (2)  of  characters 
15-19  of  CS,  and  ODEFl (3)  of  characters  20-24  of  CS .  The 
treatment  of  0DEF2  is  similar.   In  overlay-defining  both  the 
defined  variable  and  the  base  item  must  consist  entirely  of 
unaligned  data  (see  "Alignment"  below)  of  the  same  class,  but 
a  string  can  be  overlaid  onto  an  array  as  well  as  the  other 
way  round . 

Alignment 

The  declaration  of  a  variable  can  specify  an  alignment , 
either  ALIGNED  or  UNALIGNED.   An  aligned  variable  is  stored  so 
as  to  favor  speed  of  access  over  space;  typically,  storage  for 
an  aligned  variable  is  placed  at  a  word  boundary  or  other 
natural  demarcation  for  the  machine  at  hand.   An  unaligned 
variable  is  stored  so  as  to  favor  space  over  speed  of  access, 
and  is  arranged  in  storage  so  as  to  minimize  unused  space. 
The  default  alignment  for  nonvarying  strings  and  for  pictures 
is  unaligned;  for  everything  else  it  is  aligned. 

84 


cs 


1[  2|  3|  4|  5|  6|  7|  8|  9p.0[Iip.2(I3(14|15[16[I7tI8tL9|20|21[22[23t24^5|26l27l28|29|30 


ODEFl(l)   0DEF1(2)   ODEFlO) 
(a)   Overlaying  ODEFl  onto  CS 


CS 


1|  2|  3|  4|  5|  6|  7|  8|  9tL0tLliL2tL3p.4|I5[L6tL7tl8ll9|20l21^2|23|24|25|26^7l28l29|30 


OCSl  0CS2 

(b)   Overlaying  0DEF2  onto  CS 


Figure  3.   Example  of  Overlay-Defining 


85 


In  most  situations,  the  alignment  of  a  variable  has  no 
effect  on  its  behavior.   The  exception  is  that  aggregates 
composed  of  unaligned  strings  and  pictures  are  stored  with 
all  their  components  adjacent,  i.e.,  as  a  sequence  of  adjacent 
characters  (or  bits,  in  the  case  of  unaligned  bit  strings). 
The  sequence  can  then  be  used  as  a  base  item  for  overlay- 
defining. 

Initialization 

It  is  possible  to  specify  an  initialization    for  a  vari- 
able, as  long  as  its  storage  type  is  not  an  alias,  i.e.,  is 
neither  parameter  nor  defined.   The  initialization  is  specified 
using  the  INITIAL-attribute.   For  example,  in  the  declaration 

DECLARE  A (40)  FIXED  INITIAL ( (40 ) 0) ; 

the  array  A  is  initialized  to  all  zeroes.   The  initialization 
can  be  specified  by  a  single  item,  by  a  repeated  item,  or  by 
a  repeated  list,  which  can  itself  contain  items  of  these  types. 
Nesting  to  any  depth  is  permitted.  Thus 

DECLARE  B(20)  FIXED  INITIAL ( 2 , 3 ,  (  5) 4 ,  (  3)  (-1 , -2 )  )  ; 

causes  the  first  13  elements  of   B  to  be  initialized  to  the 
sequence 

2  3  4  4  4  4  4-1-2-1-2-1-2 

For  a  multidimensional   array,  initializations  are  performed 
with  the  last  subscript  varying  most  rapidly.   Thus 

DECLARE  C(3,2)  FIXED  INITIAL (1 , 2 , 3 , 4 , 5 , 6 ) ; 

86 


causes  the  initializations 


C(l,l)  =  1  C(l,2)  -  2 
C(2,l)  =  3  C(2,2)  =  4 
C(3,l)  =  5      C(3,2)  -  6 


Initialization  always  takes  place  at  the  time  of  alloca- 
tion.  Thus,  for  static  variables,  the  initialization  is 
performed  at  the  start  of  program  execution.   For  automatic 
variables,  it  is  performed  at  each  entrance  to  the  declaring 
block.   For  controlled  and  based  variables,  it  is  performed 
when  an  ALLOCATE-statement  for  the  variable  is  executed,  and 
is  applied  to  the  newly  allocated  generation.   Parameters 
and  defined  variables  cannot  be  initialized  with  the 
INITIAL-attribute . 


87 


PROCEDURES,  SCOPES,  AND  ENVIRONMENTS 

Textually,  a  procedure    is  a  body  of  code,  delimited  by 
a  PROCEDURE-statement  at  the  beginning  and  an  END-statement 
at  the  end.   Associated  with  the    procedure  are  one  or  more 
entry   points ,    each  of  which  provides  a  way  of  invoking  some 
portion  of  the  code  contained  within  the  procedure.  The  entry 
points  are  defined  by  the  PROCEDURE-statement,  as  well  as  by 
any  ENTRY-statements   that  appear  within  the  procedure.  The 
characteristics  of  an  entry  point  include  its  name,  the 
niomber  and  types  of  its  parameters,  and  the  type  of  its 
returned  value,  if  any.   Each  entry  point,  in  turn,  defines 
an  entry  constant. 

A  procedure  is  called  either  by  means  of  a  function 
reference  within  an  expression  or  by  means  of  a  subroutine 
reference  within  a  CALL-statement .   The  procedure  call  itself 
consists  of  an  entry-valued  reference  and  an  argument  list, 
possibly  empty.   For  instance,  the  procedure  call 
F(X+3, ' INVALID' )  has  an  entry-valued  reference  F  and  an 
argument  list  consisting  of  the  two  arguments  X+3  and 
'INVALID'.   The  value  of  F  must  be  an  entry  point  of  the 
procedure  being  called.   Usually  F  is  just  the  name  of  the 
procedure,  but  F  could  also  be,  for  instance,  an  entry  vari- 
able.  An  empty  argument  list  for  a  function  reference  must  be 
indicated  explicitly  by  (  ) .   If  an  entry  point  returns  a 
value,  then  it  m.ust  be  called  by  a  function  reference;  other- 
wise it  must  be  called  by  a  subroutine  reference. 

88 


An  example  of  a  procedure  definition  is: 

PI: 

P2:    PROCEDURE (QVAL, SIZE)  RETURNS (FLOAT  BINARY) ; 
DECLARE  QVAL  FLOAT  BINARY; 
DECLARE  SIZE  FIXED  DECIMAL(4); 
DECLARE  J  FIXED  BINARY; 

DECLARE  TOTAL  FLOAT  BINARY  INITIAL (0); 
DO  J  =  1  TO  SIZE; 

TOTAL  =  TOTAL  +  F(QVAL,J); 

END; 
RETURN (TOTAL) ; 
P3:    ENTRY (RVAL, RES, SIZE) ; 

DECLARE  RVAL  FLOAT  BINARY; 
DO  J  =  1  TO  SIZE; 

RES  =  RES  +  F(RVAL,J); 

END; 
RETURN 
END  PI 

This  procedure  has  three  entry  points.   PI  and  P2  are  synonymous 
(but  do  not  compare  equal) ,  and  are  entry  constants  designating 
the  entry  point  at  the  PROCEDURE-statement .   Since  that  entry 
point  returns  a   value  (with  attributes  FLOAT  BINARY) ,  PI  and 
P2  can  only  be  called  as  function  references,  i.e.,  as  compon- 
ents of  an  expression.   P3  is  the  entry  constant  naming  the 
entry  point  starting  at  the  ENTRY-statement .   It  does  not  return 
a  value,  and  so  P3  can  only  be  invoked  from  a  CALL-statement , 
e.g.,  by 

CALL  P3(A(2) ,B(2) ,22) ; 

P2  has  two  parameters,  namely,  QVAL  and  SIZE,  while  P3  has  three 
parameters,  namely,  RVAL,  RES,  and  SIZE.   As  this  example  shows, 
the  entry  points  need  not  have  the  same  parameters,  and  if  any 
parameters  are  in  common,  they  need  not  appear  in  the  same 
position.   It  is  invalid  to  reference  a  parameter  not  associated 

89 


with  the  entry  point  actually  used  to  enter  a  procedure.  For 
instance,  it  is  invalid  to  reference  RVAL  if  the  procedure  is 
entered  through  PI  or  P2. 

The  RETURN-Statement 

The  RETURN- statement  is  used  to  end  execution  of  a  proce- 
dure.  It  may  have  either  the  form 

RETURN (expr) ; 
or  the  form 

RETURN ; 

If  the  procedure  is  called  by  a  function  reference,  then  the 
RETURN-statement  must  contain  an  expression.   Conversely,  if 
the  procedure  is  called  by  a  subroutine  reference,  then  the 
RETURN-statement  must  not  contain  an  expression. 

When  a  RETURN-statement  containing  an  expression  is 
executed,  the  expression  is  evaluated.   The  value  of  the 
expression  is  then  taken  as  the  value  of  the  function  reference 
that  called  the  procedure.   If  necessary,  the  value  of  the 
expression  is  converted  to  the  type  specified  in  the  RETURNS- 
clause  of  the  entry  point  where  the   procedure  was   entered. 
A  procedure  can  return  an  aggregate  as  well  as  a  scalar. 
Moreover,  the  returned  type  may  have  asterisks  in  its  speci- 
fication ,  e.g., 

RETURNS (CHARACTER ( * ) ) ; 

for  an  entry  point  that  returns  a  character  string  of  arbitrary 
length. 

90 


Execution  of  a  RETURN-statement  not  containing  an  expres- 
sion ends  execution  of  the  procedure  and  causes  control  to 
return  to  the  point  of  call.  The  END-statement  of  a  procedure 
is  treated  as  having  an  implicit  RETURN-statement  just   in  front 
of  it,  so  that  if  control  flows  to  the  END-statement,  execution 
of  the  procedure  is  terminated.   It  is  an  error  to  allow  control 
to  flow  to  the  END-statement  of  a  procedure  that  was  called 
as  a  function  reference. 

Arguments  and  Parameters 

An  entry  point  of  a  procedure  can  have  a  sequence  of 
parameters  associated  with  it.   A  call  on  the  entry  point  must 
include  a  corresponding  sequence  of  arguments,  which  act  as 
inputs  to  the  procedure.  If 

(1)  the  argument  is  a  reference  to  a  variable  (possibly 
subscripted  or  name-qualified) ,  and 

(2)  the  attributes  of  the  argument  agree  with  those 
of  the  parameter, 

then  the  parameter  becomes  an  alias  for  the  argument,  and 
assignments  to  the    parameter  affect  the  argument.   In  all 
other  cases,  the  argument  is  considered  to  be  a  dummy.    That 
is,  when  the  call  is  made,  a  generation  of  storage  --  the 
dummy  --  is  set  aside  for  the  argument,  and  the  value  of  the 
argument  is  copied  into  that  generation.   If  the  type  of  the 
argument  disagrees  with  the  type  of  the  parameter,  the  argu- 
ment is  converted  to  the  parameter  type  and  the  converted  value 
is  assigned  to  the  dummy.   The  parameter  is  then  an  alias  for 

91 


the  dummy,  and  after  the  call  is  completed  the  dummy  is 
discarded.   Thus,  assignments  to  a  parameter  that  corresponds 
to  a  dummy  argxament  have  no  effect  at  the  point  of  call. 
Constants   and  expressions  are  always  passed  as  dummy  arguments. 

PL/I  uses  the    call-by-reference  model  of  argument 
transmission,  i.e.,  the  location  of  the  argument  is  passed  to 
the  procedure.   The  conventions  for  argument  transmission  are 
shown  by  the  following  example: 


CALLER : 


CALLEE ; 


PROCEDURE; 

DECLARE  X  FIXED  DECIMAL(5); 

DECLARE  Y  FLOAT  DECIMAL(7); 

CALL  CALLEE (X);   /*  X  IS  SET  TO  12  BY  THE  CALL  */ 

CALL  CALLEE (Y);   /*  DUMMY  CREATED,  SO  Y  IS  UNCHANGED 

CALL  CALLEE (24962) ;   /*  DUMMY  CREATED  */ 

CALL  CALLEE  (  (X.)  )  ;   /*  DUMMY  CREATED  SINCE  (X)  IS 

AN  EXPRESSSION  */ 
CALL  CALLEE (X+1 4 ) ;   /*  DUMMY  CREATED  HERE,  TOO  */ 
END  CALLER; 
PROCEDURE (P) ; 

DECLARE  P  FIXED  DECIMAL(5);   /*  P  IS  THE  PARAMETER*/ 
P  =  12; 
END  CALLEE; 


Array  sizes,  string  lengths,  and  area  sizes  of  parameters 
must  be  given  either  by  constant-valued  expressions  or  by 
asterisks.   An  asterisk  size  is  used  when  the  size  of  the 


92 


corresponding  argument  is  unknown,  or  varies  from  one  call  to 
another.   Thus  a  parameter  declared  as   CHARACTER (*)  will  match 
an  argument  declared  as  CHARACTER ( e ) ,  where  e    is  any  expression- 
However,  such  a  parameter  will  not  match  an  argument  declared 
as  CHARACTER (e)  VARYING. 

Options 

Implementation-defined  information  can  be  attached  either 
to  a  PROCEDURE-statement  or  to  the  declaration  of  an  entry 
constant  by  means  of  the  OPTIONS-attribute .   A  particularly 
common  option  (but  not  a  universal  one)  is  illustrated  by 

PROCEDURE  OPTIONS (MAIN) ; 

where  the  MAIN  option  indicates  that  execution  of  the  program 
is  to  start  with  this  procedure.   In  general,  the  information 
given  in  an  OPTIONS-attribute  affects  the  manner  in  which  the 
procedure  is  compiled. 

When  a  PL/I  procedure  references  a  procedure  written  in  a 
different  programming  language,  the  OPTIONS-attribute  can  be 
used  to  specify  the  language  of  that  foreign  procedure  so  that 
appropriate  calling  sequences  can  be  compiled.  For  instance, 

DECLARE  PRIMFN  ENTRY (FLOAT)  RETURNS (FLOAT) 
OPTIONS (FORTRAN) ; 

would  describe  a  procedure  written  in  Fortran  to  be  called  from 
a  procedure  written  in  PL/I . 


93 


Recursion 

A  PL/I  procedure  is  permitted  to  call  itself,  either 
directly  or  indirectly.   A  procedure  that  calls  itself  is 
said  to  be  reaursive ,    and  the  RECURSIVE  option  must  be 
specified  on  the  PROCEDURE-statement  of  such  a  procedure. 
An  example  of  a  recursive  procedure   is  one  that  counts  the 
number  of   nodes  in  a  binary  tree.   Each  node  is  represented 
as  a  based  structure,  and  contains  a  value,  a  left  son,  and 
a  right  son.   Each  son  is  either  itself  a  pointer  to  a  binary 
tree,  or  null.   The  procedure  in  PL/I  is: 

COUNTNODES : 

PROCEDURE (NODEPTR)  RECURSIVE  RETURNS (FIXED) ; 
DECLARE (LCOUNT,RCOUNT)  FIXED  INITIAL(O); 
DECLARE 

1  NODE  BASED (NODEPTR) , 
2  LEFT_SON  POINTER, 
2  RIGHT_SON  POINTER, 
2  VALUE  FIXED; 
DECLARE  NODEPTR  POINTER; 
DECLARE  NULL  BUILTIN; 
IF  LEFT_SON  "=  NULL  THEN 

LCOUNT  =  COUNTNODES (LEFT_SON) ; 
IF  RIGHT_SON  "=  NULL  THEN 

RCOUNT  =  COUNTNODES (RIGHT_SON) ; 
RETURN (LCOUNT+RCOUNT+1) ; 
END  COUNTNODES; 

The  procedure  is  given  a  pointer  to  a  binary  tree  as  an 
argument,  and  it  returns  the  number  of  nodes  in  the  tree  as 
its  value.   Recursiveness  is  a  property  of  a  procedure  rather 
than  of  its  entry  points,  so  that  even  if  the  recursive  call 
is  on  a  different  entry  point,  the  procedure  is  still 
considered  to  be  recursive. 


94 


The  GENERIC-Attribute 

Often  it  is  useful  to  create  a  family  of  entry  points  that 
perform  a  similar  function    but  that  expect  somewhat  different 
arguments,  and  to  assign  a  single  name  to  the  family.  The 
GENERIC-attribute  allows  a  single  name,  known  as  a  genevia 
function  ,    to  be  used  for  such  a  family  of  entry  points;  the 
choice  of  entry  points  then  depends  on  the  nature  of  the  argu- 
ments.  The  GENERIC-attribute  specifies  a  list  of  entry-point 
names,  and  associates  a  sequence  of  generalized  descriptors 
with  each  name.   A  reference  to  the  generic  function  is 
translated  into  a  reference  to  the  first  entry  point  whose 
descriptors,  as  given  by  the  GENERIC-attribute,  match  the 
arguments  of  the  generic  function.   An  asterisk  indicates  a 
descriptor  that  matches  anything.   The  test  for  descriptor 
matching  is  satisfied  if  the  descriptor  in  the  GENERIC-attribute 
is  contained  in  the  attribute  set  of  the  argument;  the  attribute 
set  can  contain  attributes  not  in  the  descriptor.   Only  data 
attributes  can  be  tested  in  this  way. 

An  example  of  a  GENERIC-attribute  is 

DECLARE  GF  GENERIC  (Gl  WHEN  (FIXED,  FIXED) , 
G2  WHEN  (FIXED,  *) , 
G3  WHEN  (*) ) ; 

Using  this  declaration,   and  assuming  the  furtl^er  declarations 

DECLARE  X  FIXED  BINARY; 
DECLARE  Y  FLOAT  DECIMAL; 

the  reference 


95 


GF(X,X+1)    translates  to   G1(X,X+1) 
The  reference 

GF(X,Y+1)    translates  to   G2(X,Y+1) 

since  the  expression  Y+1  has  data  type  float.   The  reference 

GF(X)        translates  to   G3(X) 

since  the  first  two  descriptor  sequences  each  require   two 
arguments . 

Another  application  of  the  GENERIC-attribute  is 
illustrated  by  the  declaration 

DECLARE  VARFN  GENERIC (NFl  WHEN (FLOAT (1 : 20 )  BINARY), 
NF2  WHEN (FLOAT (21: 40)  BINARY), 
NFS  WHEN  (*) ) ; 

In  this  case,  the  entry  point  represented  by  VARFN  is  selected 
on  the  basis  of  the   precision  of  the  argument,  which  is  assumed 
to  be  float  binary.   If  the  argument  has  from  1  to  20  binary 
digits,  NFl  is  used;  if  the  argument  has  from  21  to  40  binary 
digits,  NF2  is  used;  and  if  the  argument  has  more  than  40 
binary  digits,  NF3  is  used. 

Blocks  and  Scopes 

A  block  consists  of   a  sequence  of  statements,  starting 
with  a  PROCEDURE-statement  or  a  BEGIN-statement  and  extending 
to  the  matching  END-statement .   Blocks  of  either  kind  can  be 
nested.   The  primary  effect  of  the  block  structure  of  a  program 
is  to  define  the  scope  of  a  name,  i.e.,  the  set  of  statements 
from  which  the  name  can  be  referenced.   A  name  declared  in  a 

96 


DECLARE- statement  belongs  to  the  innermost  block  containing 
that  DECLARE- statement.   However,  a  name  can  also  be  declared 
by  virtue  of  its  appearance  as  a  parameter  or  as  a  statement- 
name.   A  statement-name  that  names  a  PROCEDURE-statement ,  an 
ENTRY-statement,  or  a  BEGIN-statement  belongs  to  the  block 
outside    the  one  that  contains  that  statement;  any  other 
statement-name  belongs  to  the  block  containing  the  statement 
that  it  names.   This  rule  is  needed  in  order  to  allow 
procedures  to  be  called  from  the  outside.   A  reference  to  a 
name  is  resolved  by  searching  the  nest  of  blocks  for  a  declar- 
ation of  the  name,  working  from  the  inside  out,  and  starting 
with  the  statement  containing  the  reference.     Another  way 
of  looking  at  it  is  that  the  scope  of  a  name  consists  of  the 
block  declaring  the   name  and  all  contained  blocks  except 
for  those   in  which  the  scope  is  occluded  by  an  inner  declara- 
tion of  the  same  name. 

An  example  illustrating  the  scope  of  names  is  given  in 
Figure  4.   The  parenthesized  numbers  are  used  to  distinguish 
different  declarations  of  the  same  identifier.   There  is  an 
imaginary  outer  block  used  to  hold  the  declarations  of  the 
entry  points  of  the  external  procedure  (A  and  B  in  this  case) , 
This  block  is  needed  since  the  entry  points  of  a  procedure 
belong,  not  to  the  block  of  the  procedure  itself,  but  to  the 
next  outer  block.   Since  there  is  no  such  block  for  the 
external  procedure,  one  must  be  created. 


97 


Figure 

4.   Example  Illustrating  Scope 

1 

A: 

PROCEDURE; 

2 

DECLARE  X  CHARACTER ( 1 ) ; 

3 

DECLARE  B  FIXED; 

4 

statement  sequence  1 

5 

B: 

ENTRY (Y) ; 

6 

statement  sequence  2 

7 

C: 

BEGIN  ; 

8 

DECLARE  W  FIXED; 

9 

DECLARE  Y  PICTURE  '(6)$'; 

10 

statement  sequence  3 

11 

D: 

PROCEDURE; 

12 

DECLARE  W  FLOAT  COMPLEX; 

13 

statement  sequence  4 

14 

END  D; 

15 

END  C; 

16 

E: 

PROCEDURE; 

17 

DECLARE  W  FLOAT; 

18 

Statement  sequence  5 

19 

F: 

B  =  3; 

20 

G: 

ENTRY ; 

21 

statement  sequence  6 

22 

END  E; 

23 

END  A; 

Statements  belonging  to 
different  blocks: 


Names  belonging  to 
different  blocks: 


outer 

none 

outer 

A(l) 

1,2,3,4,5,6,23 

A(l) 

C(7) 

7,8,9,10,15 

D(ll) 

11,12,13,14 

C(7) 

E(16) 

16,17,18,19,20,21,22 

D(ll) 
E(16) 

A{1)  ,  B(5) 

X(2)  ,B(3)  ,Y(5)  ,C(7)  , 

E(16) ,G(20) 

W(8) ,Y(9) ,D(11) 

W(12) 

W(17) ,F(19) 


Statements  in  scopes  of  these  names: 


A(l) 
X{2) 
B(3) 
B(5) 
Y(5) 
C(7) 
W(8) 


1-23 

1-23 

1-23 

none 

1-6,16-23 

1-23 

7-10,15 


Y(9) 

D(ll) 

W(12) 

E(16) 

W(17) 

F(19) 

G(20) 


7-15 

7-15 

11-14 

1-23 

16-22 

16-22 

1-23 


98 


Internal  and  External  Scope 

In  most  cases,  declarations  are  defaulted  to  have  internal 
scope,  meaning  that  the  declaration  designates  an  object 
distinct  from  the  objects  designated  by  other  declarations 
of  the  same  identifier.   For  instance,  if  the  variable  Q  is 
declared   in  three  different  blocks  of  a  procedure  with  the 
INTERNAL-attribute  (possibly  by  default) ,  then  each  of  these 
blocks  has  its  own  distinct  Q.   However,  several  declarations 
can  be  made  to  refer  to  the  same  object  by  giving  them  external 
scope.  External  scope  cannot  be  applied  to  just  any  declara- 
tion; it  is  restricted  to  the  static  and  controlled  storage 
classes,  and  to  named  constants.   Identifiers  declared  to  be 
external  must  necessarily  have  the  same  attributes.  As  an 
example,  if  the  declaration 

DECLARE  A (14)  STATIC  EXTERNAL; 

appears  in  two  different  blocks,  then  both  declarations  refer 
to  a  single  array.   If  an  assignment  is  made  to  A(4)  while 
executing  one  of  these  blocks,  then  the  change  will  be  visible 
in  the   other.   Declarations  of  an  external  identifer  can 
appear  both  within  a  single  external  procedure  and  among  several 
external  procedures.   The  default  scope  for  manifestly  declared 
entry  constants  is   external,  since  external  procedures  have 
to  be  declared  by  the  programmer  while  internal  procedures  are 
automatically  declared. 


99 


Entry  Values  and  Environments 

On  account  of  the  rules  for  scope  of  names  in  PL/I ,  a 
procedure  can  refer  to  names  in  blocks  surrounding  the  proce- 
dure.  Moreover,  an  entry  point  defines  an  entry  value,  and 
that  value  can  be  assigned  to  an  entry  variable  and  subsequent- 
ly invoked.   Invocation  of  the  entry  point,  in  turn,  requires 
that  references  to  outer-block  names  be  resolved  properly. 
In  order  to  achieve  this  effect,  an  entry  value  contains  not 
only  a  designation  of  an  entry  point  but  also  an  environment. 
When  the  block  surrounding  the  entry  point  is  entered,  the 
environment  of  the  entry  point  is  defined.   The  entry  value 
corresponding  to  the  entry  point  then  consists  of  the  entry 
point  itself  together  with  a  record  of  all  names  inherited 
from  outer  blocks  and  the  variables  (or  constants)  that  these 
names  denote.   In  the  case  of  recursive  procedures,  the 
environment  implicitly  designates  not  only  a  set  of  variables, 
but  also  a  recursion  level.   The  following  example  illustrates 
these  concepts : 

P :        PROCEDURE ; 

Q:        PROCEDURE (R, LEVEL)  RECURSIVE; 
DECLARE  R  ENTRY;  ■ 
DECLARE  LEVEL  FIXED; 
IF  LEVEL=10  THEN 

CALL  R(  ) ; 
ELSE  IF  LEVEL=6  THEN 

CALL  Q(S,7) ; 
ELSE 

CALL  Q(R,LEVEL+1) ; 
S:        PROCEDURE; 

PUT  DATA (LEVEL) ; 
STOP  ; 
END  S; 
END  Q; 
T:        PROCEDURE; 
END  T; 

CALL  Q(T,1) ; 
END  P;         ^QQ 


The  call  on  Q  on  the  next- to-the-last  line  initiates  a  nest 
of  recursive  calls.   On  each  call,  the  value  of  LEVEL  increases 
by  1 .   At  the  top  level,  the  entry  value  T  is  passed  as  an 
argument  to  Q;  but  since  this  entry  value  is  never  invoked, 
it  serves  only   as  a  place-holder.   At  the  sixth  level  of 
recursion,  the   entry  value  S  is  passed  as   part  of  the  recur- 
sive call  on  Q.   The  environment  of  this  entry  value  consists 
of  the  current  set  of  outer-block  variables  --  outer,  that  is, 
to  S .   In  particular,  since  S  is  internal  to  Q,  the  current 
value  of  LEVEL  —  6  in  thii.  case  --  is  part  of  the  environment 
accompanying  the  entry  constant  S.   On  subsequent  recursive 
calls,  the  entry  value  is  simply  passed  along  (cf.  the  call 
with  parameters  R  and  LEVEL+1) .   When  the  recursion  level 
reaches  10,  R  is  called.   The  value  of  R  is  the  entry  constant 
S  obtained   at  level  6,  and  so 

LEVEL  =  6 
is  printed  out  and  the  program  halts. 

The  PL/I  rules  for  block  structure,  scoping  of  names,  and 
environments  are  derived  from  Algol  60.   In  fact,  it  is  possible 
to  transcribe  the  example  above  rather  directly  into  Algol  60, 
and  the  behavior  in  Algol  60  would  be  the  same. 


101 


ON-UNITS  AND  ON-STATEMENTS 

One  of  the  more  innovative  aspects  of  PL/I  is  the  facility 
that  it  provides  for  handling  exceptional  conditions  —  the 
so-called  ON-conditions .   An  exceptional  condition  may  arise 
either  as  the  result  of  an  error,  such  as  a  subscript  out  of 
range,  or  from  an  anticipated  event,  such  as  encountering 
end-of-file  while  reading  from  a  dataset  or  reaching  the  end 
of  the  program.   PL/I  has  a  set  of  ON-conditions  corresponding 
to  these  exceptional  conditions.   When  the  condition  occurs, 
it  is  said  to  be  raised.      Using  an  ON-statement ,  the  programmer 
can  specify  a  response  to  the  condition  in  the  form  of  a 
statement  (or  begin-block)  to  be  executed.   That  response  is 
known  as  the  ON-unit.       If  no  ON-unit  has  been  specified,  a 
standard  ON-unit,  known  as  the  standard   system   action,    is 
executed.   Depending  on  the  nature  of  the  ON-condition,  it  may 
be  possible  for  the  program  to  continue  where  it  left  off  after 
the  ON-unit  is  executed.   The  different  kinds  of  ON-conditions 
are  listed  in  Table  7. 

An  example  illustrating  the  use  of  ON-conditions  and 
ON-units  is: 

P :        PROCEDURE ; 

ON  ENDFILE(SYSIN) 

GO  TO  PROCESS; 
DO  WHILE ( 'I'B) ; 

READ  FILE (SYS IN)  INTO (LINE  IMAGE); 


PROCESS: 


END; 
END  P; 


102 


c 
u 

3 
-H    O 

o 

2 


t/3  (/)  U)  WW 

(U  (U  Q)  d)      <])      O 

>i  >,  >,  >,     >,     C 


w 

en 

w 

U) 

q; 

o 

0) 

Q) 

0 

Q) 

>i 

c 

>i 

>, 

c 

>i 

+J 

C 

r. 

0) 

0) 

e 

rH 

(U  Xll 

1— 1 

•rH 

XI 

W 

nj 

03 

c 

O 

u 

04 

o 

c 


o 

c 


to 


o    o 

c    c 


o 

c 


(0 

o    <u 


o 

c 


n 

o        <u 

c  >, 


o 

c 


e 

0) 

0) 

c 

4J 

0 

U) 

•H 

> 

4J 

w  c 

•H 

0 

-a 

13  -H 

c 

V^  +J 

o 

ITJ    O 

u 

-a  < 

1 

c 

2 

nj 

o 

-p 

w 

El 


0) 
JQ 


(0 

c 

■H 

tn 


4J 

c 
i  o 

o  a: 
o  w 


•H   4J 

0 

(0  iH 
0) 


.5^ 

c 

C  0) 
OH 
■H    O 

ig  14-1 
O    D 


lO 


■p 
C 


O 
O 


0) 

M    0) 

(0 

03    O 

1— 1 

3 

fl 

10 

c 

u 

^  w 

a> 

0) 


U 
O 

a 


c 

•H 

tn 


-P 
c 

0)  a: 

i  o 

e  a 

o  a 

u  u 


I 

o 


E 
O 


0) 
4J 
V^ 

> 

c 
o 
u 


0) 

a 
o 

a 

c 


C  (U 

•H  a 

(1)  >i 

XI  -P 

3  (1) 

m  o 

>  03 


o 

a 

w 


ITJ 
C 

•H 
CO 


c 

(U 


o 
o 


4J 

3 

a 

c 


c 
o 

ro 
■P 

TJ 


c 


4J 

)-l 

ITJ 
CO 


r-i        r-i 
•H        -H 


4J 

c 

■H 
)-l 

a 

c 
o 

0) 
CP 
ITJ 

a 


T3  73 
C  C 
01     <u 


I 

c 
o 

■H 

4-) 

c 
e  (1) 

Q)    C 

a>4-i 

•H  XJ 


(1) 

E 

-rH 

4J 

I 

c 
o 

■H 
-P 

3 
U 
0) 
X 

0) 

3 
O 
0) 

c 
n3 


V) 

u 
o 
u 
u 


c 
o 

•H 

o 

IT3 


c 
o 

•H 
4J 
3 
O 
(U 
X 
0) 

E 
it3 
M 
CTi 
O 
U 

a 


c 
0 


o 


(TJ 

c 
en 

•H 

tn 


-P 

c 
a) 


o     o 
c     V 

I 


(0  -o 

U    0) 
(U  -P 

a-u 

o  -H 

E 
1-1 
0) 

a 


u 

-H 
4J 
0) 

E 
x: 


>-l    X 

(TJ    (T3 

E 

-a 

<u  en 
X  c 

•H  -H 
M-l  TJ 

OJ 
U 
X 

0) 


O     -t 

3 

0) 


0) 

3 


o 


ITJ 
C 


-p 
c 

0) 


o 
u 


0) 

>; 

0)  TI 

rH  M 

XI  O 

(0  U 

-P  (U 

a  >-i 

0) 

o  ty 
u  c 

H3  -H 

c  u 

3    3 
T) 
M 
O  T3 

0)    M  -P 


4J 

3 


ITJ   -P 
O 


3 

O 

C    I 

3  -P 


Wi  4J   > 


O  3 

a  o  a 

3  c  c 

no    0)  H 


o 
c 

en 

•H 
T) 

C 
(TJ 

^J 

c 

Q) 

E  E 

E  (1) 

O  -P 

U  H 


TI 
Q) 
U 
0) 
■P 
C 
3 
O 

o 

c 


3 

a 
c 

■H 


0)  (TJ 
(1) 
0)  Vj 
E  -P 
(tj  in 
c 

TJ 

-O    0) 
0)  -P 

N    O 

•H  0) 
C  ^1 
O^-H 
O  T) 
O  I 
0)  (T3 
U  -P 
C  ITJ 
3  T) 


Pi 

o 

Oi 


(TJ 

c 

t7^ 


C 


o 
u 

I 

(0 

(1) 
a 
o 


QJ 

E 

x: 


E     -P  E 


H  -H  3 

M    X  ^ 

(Tj    (TJ  (T3 

E  > 


4J 
ITJ 
O 


C   0) 


>*-i  TJ  XJ 
0)  IT3 
0)  -P 
O 
X 


0 


3 
in 

k4 


(1)     U) 


o 

a: 
u 


ITJ 

c 

•H 
W 


4J 

c 

OJ 


o 
o 


x: 

ty  u 
c  o 

Q)   O 

4J 

0)   c 
u 


u 

3 


O  73 
C         -P 

•H  TJ    3 


3 

o 
I 


3  -P 
O    3 


a 

c 


c 
o 

•H 
■p 

■H 

•o 

c 
o 
u 


< 

< 


Z    H 

O  >•-' 

M   -H 

Eh  ^J 

M     C 

a  0) 

2  T3 
O    H 

u  — 


z 

o 

V) 

a: 

> 

2 

O 

o 


Qi      0) 

•H        H 
<4H        >«H 

U        U 

< 


Q 
2 


a. 

Q 
2 


U     Cd 


o 

a: 
u 


X  Q 

to  Q 

t-H  U 

2  X 


103 


0) 


U 

in: 


0) 


Cm 

a: 

> 
o 


o 


o 
u 


c 
u 

(U    Q> 

o 

e  < 

o 

z 


w 

m 

w 

U) 

w 

0 

0) 

0 

(U 

0 

<u 

0) 

0) 

0 

c 

>1 

c 

>l 

c 

>1 

>1 

>1 

c 

0) 

C 
-H 
-P 

c 
o 
u 


-p 

c 

t^. 

Q) 

<D 

e 

<-{ 

0)  JDI 

rH 

■H 

XJ 

W 

(0 

tn 

c 

0 

w 

04 

e 

<D 
+J 
U) 

>1 

en  c 
o 

V4    -P 

(U    U 
TD  < 
C 
(0 
-P 

cn 


o 

a 
a 
w 


m 
c 
en 

-H 
CO 


4J 
C 
(U 


o 

o 


(0 

o    <u 

C      >i 
0)     a 

c    o 

I 

c 
o 

-H 

-p 

(0 

-p 
c 

e 


(0 

c 

en 


c 


m 
(1) 

>1 


CI.     g 

e    o 

■H       O 


c 
o 

-p 
o 

rO 

o 

c 


en 
0)     o 


o    o 


C      C 

cn    en 

•H         -rH 


-p     -P 
c    c 

0)      Q) 


O      O 


o 

c 


o 

a: 
u 


(0 

c 

cn 

■H 

cn 


c 


o 
u 


(0 

>1 


■p 
c 

0) 


o 
u 


cn 

0) 


(X 

o 
« 
Pi 
u 


03 

c 
tn 


c 

0) 


o 
u 


0) 
Xi 

<a 
hi 


c 
o 

-p 

•H 

c 

O 
U 


>i 

■P 

-H 
U 
(C 

Q.-P 
03  C 
U  QJ 
E 
cn  c 


QJ 

-a 

cn 

cn 

QJ 

■rH 

3 

0) 

cn 

(0 

O 

cn 

U 

X 

03 

M-l 

a)  o 

■H   -P 
03    (U 

>  cn 

s-i 
03 

-p 


T3 
<]) 
X 

■rH 


o 


w 

M 

cn 


dJ 
-P 

cn 

3 

m 
x: 

X 

OJ 

(U 
cn 
03 
>-i 
O 
-P 
cn 


03 


03 
> 

03 


Cn 

C 

cn    -rH 

u  -p 
c  cn 

0) 

S-i    03 
(U 

m  4-1 
(1)  o 


^ 


c 

o 


cn  -p 
CQ  u 
D    O 

cn  a, 

M-l   -P 

o  c 
(1) 

■P  4J 
C  CO 
Q)  -H 
X 

0) 

cn  c 
h  o 

03    C 


e 

3 


W      S 
2 


o    a: 

Eh      Eh 

cn    cn 


-p 
cn 
c 


cn 

T)  -P 

(U  C 

(U  0) 

O  E 

X  C 

CD  tr 

■H 

cn  cn 
c  cn 

-H    03 

>-( 

cn  o 

M-l  +J 
O  Q) 
cn 
s:  U 
-P  03 
Cn-P 
C 

(U  MH 

^  O 


w 

to 

H 

cn 
o 

2 

M 

cn 


cn 
c 
03 


-p 

3 
O 

-P 

a 

•H 
U 

cn 

X) 
3 
cn 

w 

ID 
2 

Eh 

&4 


cn 
c 

-rH 

3 
'O 

u 
o 
>-l 

S-I 
0) 

c 
o 

cn 
cn 

•H 

E 
cn 
c 

03 

u 

4J 


c 
o 

■H 
4-1 
03 
Sh 
<U 

a 
o 

-p 

3 

■P 
3 
O 
I 

4-1 

3 

c 

•H 


cu 


4-1 


cj    cn 

cn    2 

00      Eh 


Q) 

C 
(D 

o 

-p 
o 

c 

c 

03 
U 

0) 
iH 
•H 

m 


w 

M 

Q 
W 
2 

M   -^ 

W  .H 
Q  -H 

2  4-1 

ID  ^ 

104 


I 

o 
o 

■H  E 
-P  3 
OJ  E 
E  -H 
ji:  c 

■P    -rH 

■H    E 
!-l 
03 


0) 
3 
1— t 
05 
> 


X! 
03 


O    03 


-P 
C 
(U 
cn  cn 

QJ 
C    U 

o  a. 

H    0) 


>-(  +j  i-i 


o 


w 

Q 
2 

D 


O 

Jh 
<U 
N 

>i 

XJ 

c 
o 


c 

03 

-p  ^   QJ     cn 
03   -P 
O 

m  QJ  4J 

rH    OS 


T3 


QJ 
-P 

e 

QJ 
4J 

-p 

03 


W 
Q 


o 
o  a 

M    W 

cn  > 

(X  o 


> 

2 
O 

U    &H 


cn 
c 
o 


Xi 
c 
o 
o 

n 

QJ 

rH 

ja 
m 

c 

0) 


o 

Q  Cn 

w  a; 


w 

IS! 
M 

cn 
a 

2 


X  w  a 

>  H 

o  cn 


w 

Q 


Q 

o 

w 

isi 


a 

2 


o 

2 


2  U 

w  M  cn 
N  a  cQ 

M   Eh   3 

cn  cn  cn 


M  03 

Q  -H 

O  -P 

£X  -H 

U  C 

CS]  M 


0) 

C 

o 

•H 

■o 
c 
o 
o 

TJ 
OJ 

XI 

m 

0} 

•H 
T3 

>1 


01 
•rl 

-p 

■rl 
C 


In  this  case,  the  ON  ENDFILE  statement  specifies  that  when 
end-of-file  is  encountered  on  the  file  SYSIN,  the  statement 
GO  TO  PROCESS;  is  to  be  executed.   Thus  the  file  will  be  read 
up  to  its  end,  and  afterwards  the  statements  at  PROCESS  will 
be  executed. 

The  ON-Statement ,  REVERT-Statement ,  and  SIGNAL-Statement 

The  ON-statement  specifies  a  list  of  ON-conditions 
together  with  an  ON-unit.   The  association  of  the  ON-unit  with 
the  ON-conditions  is  not  made  until  the  ON-statement  is 
actually  executed.   Moreover,  execution  of  a  subsequent  ON- 
statement  can  supersede  the  effect  of  an  earlier  one.   For 
instance,  after  execution  of  the  two  ON-statements 

ON  OVERFLOW,  FIXEDOVERFLOW 

GO  TO  TOOB IC- 
ON FIXEDOVERFLOW 

GO  TO  FIXEDBIG; 

the  ON-unit  associated  with  the  OVERFLOW  condition  is 

GO  TO  TOOBIG; 
while  the  ON-unit  associated  with  the  FIXEDOVERFLOW  condition  is 

GO  TO  FIXEDBIG; 

ON-statements  have  block  scope,  in  the  sense  that  they  are 
effective  only  until  the  block  containing  them  is  terminated. 
When  execution  of  a  block  is  completed,  the  association  between 
ON-conditions  and  ON-units  reverts  to  what  it  was  in  the  previ- 
ously-executing block.   Thus  a  procedure  can  activ^*  •  t  collection 


105 


of  ON-units  appropriate  to  its  circumstances  without  affecting 
the  ON-units  set  up  by  its  caller. 

The  ON-unit  itself  can  be  either  a  BEGIN  -block  (delimited 
by  BEGIN  and  END)  or  a  single  unconditional  statement.  In 
particular,  an  ON-unit  cannot  be  either  a  DO-group  or  an  IF- 
statement.   Actual  execution  of  the  ON-unit  is  carried  out  as 
though  the  ON-unit  were  a  procedure.   In  particular,  ON-units 
carry  environments  with  them,  and  so  any  names  occurring  in  an 
ON-unit  have  the  meaning  applicable  at  the  point  of  execution 
of  the  corresponding  ON-statement . 

An  ON-statement  can  specify  the  standard  system  action  as 
an  ON-unit,  using  the  keyword  SYSTEM.   Thus 

ON  SUBSCRIPTRANGE 
SYSTEM; 

specifies  that  the  standard  system  action  is  to  be  taken  if  the 

SUBSCRIPTRANGE-condition  is  raised.   This  facility  can  be  used 

to  nullify  the  effect  of  previously  executed  ON-statements .  It 

is  also  possible  to  specify  that  a  traceback ,  or  other  debugging 

information,  is  to  be  printed  in  the  event  that  a  condition  is 

raised.   That  effect  is  gotten  by   using  the  SNAP  keyword,  as  in 

ON  SIZE  SNAP  SYSTEM; 

If  the  SIZE-condition  is  raised,  the  standard  system  action  will 
be  taken,  but  in  addition  debugging  information  will  be  printed. 
The  actual  choice  of  debugging  information  is  implementation- 
defined.  The  statement 

ON  SIZE  SNAP; 


106 


would  produce  a  different  effect:  if  the  SIZE-condition  is 
raised,  the  null-statement,  which  does  nothing,  will  be  executed, 
In  fact,  the  null-statement  is  not  a  valid  ON-unit  for  the 
SIZE-condition  because  it  does  not  terminate   in  a  GOTO- 
statement.   The  question  of  validity  of  such  ON-units  is 
discussed  below. 

The  REVERT-statement  can  be  used  to  cancel  the  effect  of 
an  ON-statement ,  or  several  of  them,  without  knowing  what  ON- 
condition  was  in  effect  previously.  The  REVERT-statement  speci- 
fies a  list  of  ON-conditions .  Execution  of  the  REVERT-statement 
causes  the  ON-unit  for  each  of  these  conditions  to  revert  to 
what  it  was  in  the  previously-executing  block.   Thus,  in  the 
sequence: 

ON  ENDFILE(SYSIN) 

CALL  ENDER; 
BEGIN; 

ON  ENDFILE(SYSIN) 
GO  TO  ALT_END; 

REVERT  ENDFILE(SYSIN) ; 

END; 

the  REVERT-statement  causes  the  on-condition 

CALL  ENDER; 

to  again  be  associated  with  the  ENDFILE-condition  for  the  file 
SYSIN. 

The  SIGNAL-statement  is  used  to  raise  a  specified  ON- 
condition.   For  example,  the  statement 

SIGNAL  ZERODIVIDE; 
107 


causes  the  ZERODIVIDE-condition  to  be  raised  and  the  appropri- 
ate ON-unit  (possibly  the  standard  system  action)  to  be  invoked. 
This  statement  is  particularly  useful  in  debugging  program  logic 
for  handling  ON-conditions .   It  is  also  the  only  way  to  raise 
a  programmer-defined  condition  (discussed  below) . 

Enablement  and  Disablement 

A  number  of  the  ON-conditions  require  time-consuming  code 
(on  most  machines,  at  least)  in  order  to  check  whether  or  not 
they  have  occurred.   The  time  needed  to  check  whether  a  sub- 
script is  out  of  range,  for  instance,  well  may  dominate  the 
time  needed   for  the  retrieval  of  a  subscripted  variable.  There- 
fore PL/I  allows  the  programmer  to  either   enable  (turn  on)  or 
disable  (turn  off)  the  check.   Enablement  and  disablement  are 
provided  only  for  certain  ON-conditions.  They  are  specified  by 
means  of  a  condition   prefix,    which  consists  of  either  an  ON- 
condition  name  or  the  negation  of  an  ON-condition  name,  in 
parentheses  and  followed  by  a  colon.   The  condition  prefix  can 
be  applied  either  to  a  single  statement  or  to  a  block.  For 
example,  in  the  sequence: 

(OVERFLOW, NOS I ZE) : 
P:  PROCEDURE; 

(NOOVERFLOW) :   Q  =  A  +  BTR ( I ) ; 

END; 

the  SIZE-condition  is  disabled  throughout  the  procedure  P,  while 
the  OVERFLOW-condition  is  enabled  throughout  P   except  for  the 


108 


single  statement  where  NOOVERFLOW  is  indicated.   For  that 
statement,  the  condition  is  disabled,  and  no  test  will  be  made 
for  it.   If  a  condition  is  raised  in  a  statement  where  it 
has  been  disabled,  that  is  considered  to  be  a  programmer  error, 
and  the  implementation  is  not  to  be  held  responsible  for  its 
consequences . 

Enablement  and  disablement  are  static  properties  of  a 
program.   In  other  words,  it  is  possible  to  tell  whether  a 
particular  ON-condition  is  either   enabled  or  disabled  for  a 
particular  statement  just  by  looking  at  the    program,  without 
considering  what  its  sequence  of  execution  is.  In  this  respect, 
enablement  and  disablement  differ  from  the  ON-statements , 
whose  execution  depends  on  program  flow.   Enablement  and  dis- 
ablement affect  whether  a  condition  is  or  is  not  to  be  tested 
for,  while  ON-statements  determine  what  action  is  to  be  taken 
if  the  condition  is  raised.   The  statement 

ON  UNDERFLOW; 

does  not  disable  the  UNDERFLOW-condition;  it  merely  states  that 
if  that  condition  is  detected,  the  null-statement  is  to  be 
executed. 

Builtin  Functions  for  ON-Conditions 

During  the  execution  of  an  ON-unit,  a  number  of  builtin 
functions  are  available  in  order  to  determine  the  circumstances 
that  caused  the  corresponding  ON-condition  to  be  raised.  Some 
of  these  apply  to  all  ON-conditions  and  are  discussed  here; 


109 


other  are  specific   to  particular   ON-conditions  and  are 
discussed  in  connection  with  those  conditions.  In  general, 
these  builtin  functions  do  not  have  meaningful  values  except 
in  the  context  of  an  ON-unit.   They  are  all  functions  of  no 
arguments . 

The  ONCODE  builtin  function  has  as  its  value  an  imple- 
mentation-defined integer  used  to  indicate  why  the  active  ON- 
condition  was  raised.   A  particular  condition  may  have  more 
than  one  code  value  associated  with  it.   One  common  convention 
is  that  the  value  of  ONCODE  is  zero  if  the  ON-condition  was 
raised  by  a   SIGNAL-statement .   The  ONLOC  builtin  function 
returns  as  its  value  the  name  of  the  innermost  entry  point 
active  when  the  condition  was  raised.   For  input-output-related 
ON-conditions,  the  ONFILE  builtin  function  has  as  its  value 
the  name  of  the  file  that  was  being  operated  upon  when  the  con- 
dition was  raised.   The  values  of  both  ONLOC   and  ONFILE  are 
in  the  form  of  character  strings. 

Categorization  of  the  ON-Conditions 

The  various  ON-conditions  listed  in  Table  7  can  be  broken 
down  into  three  groups.   The  first  group  consists  of  the  compu- 
tational ON-conditions.   Most  conditions  in  this  group  are 
raised  in  response  to  a  particular  kind  of  error.   The  computa- 
tional ON-conditions  are  the  only   ones  that  can  be  enabled 
and  disabled.   They  are: 


110 


CONVERSION 

FIXE DOVE RFLOW 

OVERFLOW 

SIZE 

STRINGRANGE 

STRINGSIZE 

SUBSCRIPTRANGE 

UNDERFLOW 

ZERODIVIDE 

The  occurrence  of  one  of  these  conditions  usually  means  that  a 
bad  result  has  been  generated,  and  so  the  active  computation 
cannot  be  continued.   For  this  reason,  the  ON-units  associated 
with  most  of  these  conditions  must  not  terminate  normally, 
i.e.,  must  cause  a  transfer  of  control  out  of  the  ON-unit  by 
means  of  a  GOTO- statement  or  similar  construction.  Normal 
termination  would  mean  that  the  active  computation  would  be 
resumed,  and  the  nature  of  the  condition   is  such  that  the 
computation  cannot  be  resumed.   For  instance,  if  a  subscript 
is  out  of  range  on  an  array  reference,  there  is  no  way  to 
obtain  an  appropriate  value  for  the  reference. 

Three  of  the  computational  ON-conditions  are  treated 
somewhat  differently.   The  CONVERSION-condition  is  raised  when 
data  is  being  converted  from  character  to  some  other   type. 
When  this  condition  is  raised,  the  programmer  can  modify  the 
character  string  to  be  converted.   If  a  normal  return  takes 
place  from  the  ON-unit,  i.e.,  the  ON-unit  completes  without 
a  transfer  of  control,   the  conversion  is  reattempted  with  the 
modified  input  string. 

Two  builtin  functions  are  available  for  the  modification: 
ONSOURCE  and  ONCHAR .   ONSOURCE  has  as  its  value  the  character 
string  to  be  converted,  while  ONCMAR  has  as  its  value  the  left- 
Ill 


most  character  in  that  string  for  which  no  valid  continuation 
exists.   By  examining  ONSOURCE  and  ONCHAR,  the  programmer  may 
be  able  to  determine  the  difficulty  and  what  to  do  about  it. 
Moreover,  ONSOURCE  and  ONCHAR  can  be  used  on  the  left  side  of 
an  assignment  (within  an  ON-unit  for  the  CONVERSION-condition) , 
and  so  the  string  to  be  converted  can  be  modified  by  assign- 
ments to  either  ONSOURCE  or  ONCHAR  (which  can  also  be  used  as 
pseudovariables) .   For  example, if  a  character  string  is  being 
converted  to  a  bit  string   the  following  ON-unit  might  be 
appropriate: 

ON  CONVERSION  BEGIN; 

DECLARE  ONCHAR  BUILTIN; 
IF  0NCHAR='fe5'  THEN 

ONCHAR  =  • 0 ' ; 
ELSE  ONCHAR  =  ' 1 ' ; 
END; 

If  the  string  to  be  converted  does  not  consist  entirely  of  ones 
and  zeros,   each  blank  in  that  string  will  be  replaced  by  a  zero, 
and  each  other  deviant  character  will  be  replaced  by  a  one. 

The  UNDERFLOW-condition  also  receives  slightly  different 
treatment.   Normal  return  from  the  UNDERFLOW-condition  is 
permitted,  and  the  value  of  the  computation  that  underf lowed  is 
taken  to  be  zero.  The  STRINGSIZE-condition  arises  when  a  string 
is  shortened  as  a  result  of  a  conversion  or  assignment.   Upon 
normal  return  from  the  ON-unit,  the  string  is  truncated  on  the 
right  to  the  required  length.   Since  the  standard  system  action 
in  this  case  is  to  do  nothing,  this  condition  is  often  ignored. 
However,  it  can  be  used  in  either  of  two  ways.  If  it  is  disabled, 
then  the  compiler  need  not  produce  code  to  check  for  string 

112 


overflow.   Moreover,  if  a  nonstandard  ON-unit  is  provided, 
then  the  programmer  can  take  some  action.   However,  there  is 
no  way  that  the  programmer  can  modify  the  result  produced 
either  for  the  UNDERFLOW-condition  or  for  the  STRINGSIZE- 
condition . 

The  second  group  of  ON-conditions  is  the  input-output 
conditions.   Each  of  these  conditions  is  associated  with  a 
particular  file,  specified  along  with  the  condition  name. 
The  input-output  conditions  are: 

ENDPAGE 

ENDFILE 

KEY 

NAME 

RECORD 

TRANSMIT 

UNDEFINEDFILE 

Some  of  these  conditions  are  discussed  further  in  connection 
with  input-output. 

The  remaining  conditions  are  more  varied.  These  are: 

AREA 

CONDITION 

ERROR 

FINISH 

STORAGE 

The  AREA-condition  is  raised  when  an  allocation  is  attempted  in 
an  area,  and  there  is  insufficient  space  for  the  allocation. 
If  the  associated  ON-unit  returns  normally,  the  area-reference 
in  the  ALLOCATE-statement  is  reevaluatd,  and   the  allocation  is 
reattempted.   Therefore  an  appropriate  response  to  the  AREA- 
condition  is  to  assign  a  new  area  value  to  the  area  variable 

referenced  in    the   ALLOCATE-statement. 

113 


The  programmer  can  define  ON-conditions  using  the  keyword 
CONDITION  and  an  identifier,  known  as  the  condition-name ,    ON- 
units  can  be  provided  for  programmer-named  conditions,  but  they 
can  only  be  raised  by  a  SIGNAL-statement .  For  instance,  a 
programmer  might  write: 

ON  CONDITION (TABLE_OVERFLOW) 
CALL  OVERFLOW_RECOVERY ; 

and  then,  in  some  other  part  of  the  program,  write: 

IF  T  >  TABSIZE  THEN 

SIGNAL  TABLE_OVERFLOW; 

The  ERROR-condition  is  raised  under  a  variety  of  circum- 
stances, some  of  which  can  be  implementation-defined.  The 
standard  system  action  in  response  to  a  number  of  other  ON- 
conditions  is  to  comment  (i.e.,  display  diagnostic  information) 
and  then  to  raise  the  ERROR-condition.   (It  is  quite  acceptable 
to  have  an  ON-unit  raise  an  ON-condition  itself.) 

The  FINISH-condition  is  raised  when  the  program  completes. 
It  differs  from  all  other  conditions  in  that  it  is  raised  as 
a  normal  aspect  of  program  execution.   The  STORAGE-condition  is 
raised  when  the  program  runs  out  of  storage.   Since  programs 
consume  storage  in  many  different  ways,  the  exact  circumstances 
under  which  it  is  raised  are  implementation-defined.  Recovery 
from  this  condition  may  or  may  not  be  possible. 


114 


OTHER  STATEMENTS  AFFECTING  FLOW  OF  CONTROL 

Conditional  Statements 

The  conditional  statement  is  used  in   order  to  test  a 
condition  and  take  some  action  depending  on  the  result.  A 
conditional  statement  starts  with  an  IF-statement ,  specifying 
the  test,  and  may  include  an  ELSE-part  that  specifies  what 
action  to  take  if  the  test  fails.   For  instance,  the  sequence 

IF  Q  <=  QMAX  THEN 

INDEX  =  INDEX+1; 
ELSE 

GO  TO  PART_7; 

causes  the  assignment 

INDEX  =  INDEX+1; 

to  be  executed  if  the  condition  Q  <=  QMAX  is  true,  and  the 
statement 

GO  TO  PART_7 

to  be  executed  otherwise.   The  statement  following  either  THEN 
or  ELSE  can  itself  be  a  conditional  statement,  so  that  nests 
of  conditional  statements  can  be  built  up.   Moreover,  either 
THEN  or  ELSE  can   be  followed  by  a  DO-group  (discussed  below) , 
so  that  several  statements  can  be  executed  after  the  test 
rather  than  just  one.   An  example  of  a  more  complicated 
conditional  statement  is: 


115 


IF  A(I)=0  THEN  DO; 

SIZE1=SIZE1+INCR; 

SIZE2=SIZE2-INCR; 

IF  SIZE2<SIZE1  THEN 
CALL  ADJUST; 

END; 
END  IF  A (I) >0  THEN 

SIZE2=SIZE2+INCR; 
ELSE 

SIZE1=SIZE1-INCR; 


It  is  not  necessary  that  each  IF-statement  have  a  corresponding 
ELSE- statement .   In  complicated  conditional  statements,  each 
ELSE  is  paired  with  the  nearest  preceding  unpaired  IF,  working 
from  front  to  back. 

The   test  in  an  IF-statement  actually  takes  the  form  of 
an  expression,  which  is  evaluated  and  converted  to  a  bit 
string.   Since  the  comparison  operators  all  produce  one-bit 
results,  and  since  the  logical  operators  also  produce  one-bit 
results  when  their  operands  are  one-bit  values,  the  conversion 
is  usually   unnecessary.   If  the  bit  string  obtained  by 
evaluating  the  test  expression  has  at  least  one  one-bit  in  it, 
the  test  succeeds,  and  otherwise  it  fails.   The  test  expression 
must  be  scalar-valued,  although  if  it  is  not  scalar-valued  the 
SOME  and  EVERY  builtin  functions  can  be  used  to  reduce  it  to 
a  scalar  value . 

The  DO- Statement 

The  DO-statement  has  three  main  variants:  the  simple  DO, 
the  DO-WHILE,  and  the  specified  DO.   The  simple  DO  is  used  in 
order  to  convert,  syntactically,  a  sequence  of  statements  into 
a  single  statement.   A  simple  DO-group  has  the  form: 

116 


DC- 
Statement- sequence 
END; 

The  statements  in  the  sequence  are  executed  just  once.  Transfers 

of  control  into  and  out  of  the  sequence  are  permitted.  The  main 

use  of  the  simple  DO-group  is  as  part  of  a  conditional  statement, 
The  DO-WHILE   variant  has  the  form: 

DO  WHILE (expression) ; 

A  DO  WHILE-group  consists  of  a  DO  WHILE-statement  followed  by 
a  statement  sequence  and  a  matching  END-statement .  The  state- 
ments in  the  group  are  executed  repeatedly,  and  the  expression 
in  the  DO  WHILE-statement  is  tested  before  each  execution.  If 
the  test  fails,  control  is  transferred  to  the  statement  follow- 
ing the  group.  If  the  expression  is  initially  false,  the  group 
is  not  executed  at  all.   An  example  of  a  DO  WHILE-group  is: 

DO  WHILE (CVAL>0) ; 

DVAL=DVAL+G(CVAL) ; 
CVAL=CVAL-DVAL ; 
END; 

The  specified  DO  itself  has  a  number  of  variants.  As  with 
the  other  two  forms,  a  DO-group  consists  of  a  specified  DO- 
statement  followed  by  a  statement  sequence  followed  by  an  END- 
statement.   The  most  common  variant  is  illustrated  by: 

DO  M  =  0  TO  100  BY  2; 

In  this  case  the  statements  in  the  group  are  executed  repeatedly. 
Before  the  first  execution,  M  is  assigned  the   value  0.  M  is 
then  increased  by  2  on  each  execution    of  the  group,  and  has 


117 


the  value  100  on  the  last  execution  of  the  group.  Upon  comple- 
tion of  the  entire  group,  the  value  of  M  is  102.   However, 
transfer  out  of  the  group  is  permitted,  and  if  that  happens, 
M  retains  the  value  assigned  to  it  on  the  most  recent  iteration. 

The  TO-clause  and  the  BY-clause  can  be  written  in  either 
order,  and  either  of  them  can  be  omitted.   If  the  TO-clause  is 
omitted,  the  group  is  iterated  indefinitely,  i.e.,  until  a 
transfer  of  control  out  of  the  group  takes  place.   If  the  BY- 
clause  is  omitted,  a  value  of  1  is  assumed  for  it.   On  each 
iteration,  the  control  variable  (the  variable  following  the 
keyword  DO)  is  incremented  by  the  value  given  in  the  BY-clause. 
If  the  BY-clause  has  a  negative  value,  then  the  control  vari- 
able is  decremented  rather  than  incremented.  The  loop  terminates 
when  the  value  of  the  control  variable  is  greater  than  the 
value  of  the  TO-clause  (for  a  positive  BY-value)  or  less  than 
the  value  of  the  TO-clause  (for  a  negative  BY-value) .  If  the 
termination  test   is  satisfied  by  the   initial  value  of  the 
control  variable,  the  group  is  executed  zero  times.  If  neither 
the  TO-clause  nor  the  BY-clause  appears,  the  group  is  executed 
for  a  single  value  of  the  control  variable. 

The  TO-clause  and  the  BY-clause  are  both  evaluated  prior 
to  execution  of  the  statements  within  the  DO-group.  Thus  any 
changes  to  values  of  variables  that  appear  within  the  TO-clause 
or  the  BY-clause  have  no  effect  once  the  iteration  has  started. 
The  control  variable  need  not  have  arithmetic  type;  a  string  or 
pictured  type  is  also  acceptable.  A  WHILE-clause  can  also  be 
specified,  e.g . , 

118 


DO  JV  =  X  BY  Y  WHILE (PROP (JV)< PROP (JV+1) ) ; 
Another  variant  is  illustrated  by: 

DO  P  =  LIST_HEAD  REPEAT  (P->NEXT)  WHILE  (P"'=NULL)  ; 
or 

DO  STRING=''  REPEAT (STRING  I  I  CHARS (I) )  WHILE (LENGTH (STRING) < LMAX)  ; 

The  control  variable  is  assigned  the  given  initial  value  on 
the  first  iteration.   On  subsequent  iterations,  the  value  of 
the  REPEAT-clause  is  recalculated  and  assigned  to  the  control 
variable.   The  WHILE-clause  can  be  omitted,  although  usually 
it  is  desirable  to  include  it. 

The  specified  DO  can  consist  of  a  sequence  of  specifications 
rather  than  a  single  one.   For  instance,  the  statement 

DO  M  =  3,7,M+2  BY  3  TO  15,0; 

executes  the  group  of  statements  that  follows  for  the  sequence 
of  values  3,  7,  9,  12,  15,  0.  Each  specification  in  the  group 
can  have  the  general  forms  described  above. 

The  GOTO-statement 

The  GOTO-statement  causes  control  to  be  transferred  to 
the  label  specified  in  the  statement.   The  statement  actually 
specifies  a  label-valued  expression,  and  although  that  expres- 
sion normally  is  a  constant,  i.e.,  a  statement-name,  it  need 
not  be.   For  instance,  it  could  be  a  subscripted  reference  to 
an  array  of  statement-names,  so  that  the  appropriate  destina- 
tion is  selected  by  the  value  of  an  index. 


119 


The  destination  of  a  GOTO-statement  need  not  be  in  the 
same  block  as  the  statement  itself.   If  the  destination  is 
in  a  different  block,  then  the  effect  of  the  statement  is  to 
terminate  execution  of  the  current  block  and  all  blocks 
between  the  statement  and  its  destination.   In  other  words, 
at  the  moment  when  the  GOTO-statement  is  executed,  there  will 
be  a  hierarchy  of  active  blocks,  with  the  current  block  last 
in  the  hierarchy.   The  label  value  obtained  from  the  GOTO- 
statement  must  designate,  as  its  environment  component,  some 
block  in  the  hierarchy.   Then  all  blocks  between  the  designated 
block  and  the  current  block,  as  well  as  the  current  block 
itself,  are  terminated.   The  designated  block  then  becomes 
the  current  block,  and  control  is  transferred  to  the  statement 
named  by  the  label  value. 

A  GOTO-statement  whose  destination  is  not  in  the  same 
block  as  the  statement  itself  is  called  a  nonlocal    goto. 
A  nonlocal  goto  is  expensive  to  execute  relative  to  a  local  one. 
Therefore  the  programmer  is  allowed  to  declare  the  LOCAL- 
attribute  for  a  label  variable.   The  LOCAL-attribute  consti- 
tutes a  claim  by  the  programmer  that  any  GOTO-statement  using 
the  value  of  that  label  variable  will  be  a  local  goto.  Thus 
the  compiler  need  not  examine  the  environment  associated  with 
the  label,  and  can  generate  instructions  to  execute  the 
requested  transfer  of  control  directly. 


120 


The  STOP-Statement  and  the  Null-Statement 

The  STOP-Statement  has  the  form 

STOP  ; 
and  is  used  to  stop  execution  of  the  program.  It  has  the  effect 
of  terminating  the  execution  of  all  currently  active  blocks. 

The  null-statement  has  no  text  at  all;  it  is  written  as 
just  a  semicolon.   Its  main  uses  are  to  place  a  statement-name, 
to  fill  out  a  branch  of  a  conditional  statement  where  no  action 
is  to  be  taken,  and  to  specify  that  no  action  is  to  be  taken  in 
response  to  a  specified  ON-condition . 


121 


FILES  AND  RECORD  INPUT-OUTPUT 
File  Attributes 

The  attributes  of  a  file  determine  the  kinds  of  operations 
that  can  meaningfully  be  applied  to  that  file.  Moreover,  they 
dictate  to  some  extent  the  characteristics  of  the  dataset 
associated  with  the  file.   The  final  determination  of  file 
attributes  takes  place  when  the  file  is  opened,  i.e.,  associ- 
ated with  a  dataset.   If  a  file  is  opened  and  closed   several 
times,  it  can  have  different  attributes  at  different  openings. 

The  file  attributes  INPUT,  OUTPUT,  and  UPDATE  determine 
the  direction  of  information  flow  in  an  obvious  way.  An  opened 
file  must  have  either  the  RECORD-attribute  or  the  STREAM- 
attribute.   A  record  file  is  associated  with  a  dataset  con- 
sisting of  a  sequence  of  reaords ,    which  are  read  or  written 
as  single  units.   A  record  may  or  may  not  have  a  key    associ- 
ated with  it.   If  it  does,  then  the  file   has  the  KEYED- 
attribute.   If  the  records  are  sequenced,  i.e.,  the  notion  of 
"next  record"  is  meaningful,  then  the  file  has  the  SEQUENTIAL- 
attribute;  otherwise  it  has  the  DIRECT-attribute .  A  direct 
file  is  necessarily  keyed,  since  without  a  key  there  is  no 
way  to   designate  a  record  within  the  file,  while  a  sequential 
file  may  or  may  not  be  keyed. 

A  stream  file  is  associated  with  a  dataset  consisting  of 
a  sequence  of  characters.   Within  the  sequence  of  characters, 
linemarks ,    pagemarks ,      and  carriage-returns    can  appear.   A 
linemark  marks  the  break  between  the  characters  on  two  succes- 


122 


sive  lines  (either  input  or  output) .   A  pagemark  is  meaningful 
only  for  a  dataset  that  is  to  be  printed,  and  marks  the  start 
of  a  new  page.   A  carriage-return  is  meaningful  only  for  a 
dataset  that  is  to  be  printed,  and  indicates  that  the  follow- 
ing line  is  to  be  overprinted,  i.e.,  printed  without  advancing 
the  carriage.   A  file  associated  with  a  dataset  to  be  printed 
has  the  PRINT-attribute ,  and  consequently  the  OUTPUT-attribute 
also. 

A  file  may  also  have  an  ENVIRONMENT-attribute  associated 
with  it.   The  ENVIRONMENT-attribute  contains  implementation- 
defined  information  describing  the  associated  dataset.  Typical 
items  found  in  the  ENVIRONMENT-attribute  are  record  length, 
blocking  factors,  record  formats,  character  set  selection,  and 
tape  densities. 

File  Opening  and  Attribute  Determination 

A  file  can  be  opened  either  explicitly,  through  execution 
of  an  OPEN-statement,  or  implicitly,  through  execution  of  some 
other  input-output  statement  referencing  an  unopened  file.  When 
the  file  is  opened,  any  attributes    given  in  the  file  declara- 
tion are  combined  with  those  given  in  the  OPEN-statement.  The 
resulting  partial  set  of  attributes  is  then  checked  for  consis- 
tency.  If  any  inconsistency  is  found,  the  UNDEFINEDFILE- 
condition  is  raised  for  the  file.  Then  defaulting  rules  are 
applied  to  generate  a  complete  set  of  file  attributes.  The 
possible  sets  of  file  attributes  are  given  in  Table  8.  For 


123 


TABLE  8.  Complete  Sets  of  File  Attributes 


STREAM  INPUT  FILE 

STREAM  OUTPUT  FILE 

STREAM  OUTPUT  PRINT  FILE 

STREAM  INPUT   SEQUENTIAL  FILE 

RECORD  INPUT   SEQUENTIAL  KEYED  FILE 

RECORD  INPUT   DIRECT  KEYED  FILE 

RECORD  OUTPUT  SEQUENTIAL  FILE 

RECORD  OUTPUT  SEQUENTIAL  KEYED  FILE 

RECORD  OUTPUT  DIRECT  KEYED  FILE 

RECORD  UPDATE  SEQUENTIAL  FILE 

RECORD  UPDATE  SEQUENTIAL  KEYED  FILE 

RECORD  UPDATE  DIRECT  KEYED  FILE 


Note:    The  ENVIRONMENT-attribute   may  be  added 
to  any  of  these  combinations. 


124 


example,  in  the  sequence 

DECLARE  CHANGES  INPUT  FILE; 
OPEN  FILE (CHANGES)  KEYED; 

the  initial  set  of  attributes  used  for  opening  the  file  CHANGES 
is  INPUT  KEYED.  Since  these  attributes  are  consistent  with 
each  other,  the  opening  can  proceed.   The  RECORD  and  FILE 
attributes  are  implied  by  the  KEYED  attribute,  and  so  these 
are  added  to  obtain  the  set   RECORD  INPUT  KEYED  FILE.  Although 
both   DIRECT  and  SEQUENTIAL  are  consistent  with  this  set,  the 
default  choice  is  SEQUENTIAL,  and  so  SEQUENTIAL  is  added  to 
obtain  the  complete  set 

RECORD  INPUT   SEQUENTIAL  KEYED  FILE 

The  FILE-option  in  an  OPEN-statement  contains  a  file-valued 
expression,  which  is  either  a  constant  or  something  that 
evaluates  to  a  file  constant.   It  is  assumed  that  each  dataset 
has  a  name  (typically,  known  to  the  surrounding  operating 
system)  and  so  the  file  opening  has  to  specify  the  name  of  the 
dataset  to  be  linked  to  the  file.   The  name  can  be  specified 
by  a  TITLE-option;  if  it  is  not,  the   name  of  the  file  constant 
obtained  by  evaluating  the  FILE-option  is  used.  Thus  the 
statements 

DECLARE  CHANGENAME  CHARACTER (4 ) ; 

CHANGENAME  =  '04  37' ; 

OPEN  FILE (CHANGES)  TITLE (CHANGENAME)  STREAM  INPUT; 

OPEN  FILE(OLDSET)  RECORD  INPUT  DIRECT; 

cause  the  file  CHANGES  to  be  associated  with  the  dataset  C4  37, 

and  cause  the  file  OLDSET  to  be  associated  with  the  dataset  OLDSET. 

125 


For  stream  files,   other  information  can  be  given  in  the 

OPEN-statement .  For  instance, 

OPEN  FILE(LISTING)  PRINT  LINESIZE (110 )  TAB ( 10 , 40 , 70 ) 
PAGESIZE(50) ; 

causes  the  file  LISTING  to  be  opened  with  the   understanding 
that  a  new  line  will  be  started  after  at  most  110  characters, 
a  new  page  will  be  started  after  at  most  50  lines,  and  tabstops 
(discussed  under  "Edit-Directed  Input-Output"  below)  will  be 
placed  at  column  positions  10,  40,  and  70.   Were  any  of  these 
values  to  be  omitted,  implementation-defined  values  would  be 
assumed. 

If  an  input-output  statement  is  executed  on  a  closed  file, 
then  the  file  is  implicitly  opened.   For  instance,  if  the 
statement 

PUT  FILE (ANS) (M,N) ; 

is  executed  and  the  file  ANS  is  not   open,  the  file  will  be 
opened  with  the  implicit  attributes  STREAM  and  OUTPUT. 
Similarly,  if  the  statement 

DELETE  FILE(INV)  KEY (PART_NAME) ; 

is  executed  and  the  file  INV  is  not  open,  it  will  be  opened  with 
the   implicit  attributes  RECORD  and  UPDATE.  The  implicit  attri- 
butes are  treated  as  though  they  appeared  on  an  OPEN-statement, 
so  any  attributes  given  in  the  declaration  of  the  file  are 
combined  with  those  derived  from  the  implicit  opening. 


126 


File  Closing 

Just  as  the  OPEN-statement  creates  the  connection  between 
a  file  and  a  dataset,  the  CLOSE-statement  breaks  the  connection. 
All  files  are  automatically  closed  at  program  termination.  The 
programmer  can  also  specify  actions  such  as  dataset  disposition 
by  means  of  an  ENVIRONMENT-attribute  attached  to  the  CLOSE- 
statement,  e.g., 

CLOSE  FILE(BIBLIO)  ENVIRONMENT (REWIND)  ; 

A  file  can  be  close!  and  later  reopened  with  different  attributes 
An  attempt  to  open  a  file  that  is  already  open,  or  to  close  a 
file  that  is  closed,  has  no  effect. 

Operations  on  Record  Files 

There  are  five  statements  applicable  to  record  files: 

READ 

WRITE 

LOCATE 

REWRITE 

DELETE 

Each  of  these,  in  turn,  has  a  number  of  clauses  that  can  be 
applied  to  it.   The  attributes  of  a  file  determine  which  state- 
ments and  clauses  can  meaningfully  be  applied  to  that  file. 
For  instance,  READ  cannot  be  applied  to  an  output  file,  DELETE 
can  only  be  applied  to  an  update  file,  and  any  clause  that 
references  a  key  can  only  be  applied  to  a  keyed  file.  The 
meanings  of  the  statements  are  summarized  in  Table  9,  and  the 
meanings  of  the  clauses  are  summarized  in  Table  10.  Table  11 


127 


Table  9.   Record  Input-Output  Statements 


READ 


REWRITE 


WRITE 


LOCATE 


DELETE 


read  a  record  from  a  dataset 
(input  and  update  files  only) 
replace  a  record  on  a  dataset 
(update  files  only) 
add  a  record  to  a  dataset 
(output  and  update  files  only) 
obtain  buffer  space  for  a  record 
(output  files  only) 
delete  a  record  from  a  dataset 
(update  files  only) 


Table  10.   Clauses  on  Record  Input  Output  Statements 


FILE 
INTO 

FROM 

KEY 

KEYFROM 

KEYTO 

IGNORE 

SET 


specifies  the  file  accessed  by  this  statement 

specifies  the  generation  to  receive  a  record 

being  read 

specifies  the  generation  containing  a  record 

to  be  written 

specifies  a  record  to  be  read,  replaced, 

or  deleted 

specifies  the  source  for  a  key  to  be  attached 

to  a  record  to  be  written 

specifies  where  to  put  the  key  associated 

with  a  record  being  read 

specifies  the  number  of  records  to  be  skipped 

by  execution  of  a  read  statement 

specifies  where  to  put  a  pointer  to  a  newly 

created  generation 


128 


< 
Q 

3 

Q 

X 

X 

X 

k  J 
•^ 

X 

- 

X 

X 

X 

X 

X 

X 

X 

X 

X 

X 

X 

X 

X 

X 

X 

X 

in 

X 

X 

X 

X 

X 

X 

X 

X 

X 

X 

Eh 

D 

Eh 
D 
O 

Q 

X 

X 

C/3 

X 

X 

w 

X 

X 

Eh 

D 

cu 

2 

M 

Q 

X 

X 

X 

X 

CO 

it: 

X 

X 

X 

X 

X 

X 

X 

X 

X 

w 

X 

X 

X 

c 

■P 
ro 

4-1 

W 

«w 
O 

E 

0 

z 

M 

u 

M 

Q 

< 

>> 
u 
u: 

z 

M 

14-1 
U 

M 

Q 

< 

>H 

u 
:<: 

o 

£h 
Z 

M 

M 

Q 

a 

Eh 

u 
w 

u-i 
u 

M 

Cm 

a 
< 

>H 

u 
:<: 

a 

Eh 

u 
w 

u 

M 

u. 

Q 
< 

u 
t< 

a 

Eh 
U 
W 

U-l 

►J 

M 

Ci4 

Q 

< 

u 
o: 
o 

2 

U 

M 

u 
J 

M 

Ci4 

Q 

< 

u 

u 
a: 
o 
o 
z 

M 

U 
M 

Q 

M 

O 
Eh 

>H 

U 

o 
z 

o 

M 
X-l 

U 

M 
Cl, 

Q 

<: 

- 

o 
a: 

<4H 

u 

►4 

M 

Cm 

CO 
Eh 
M 

q: 
2 

i 

PS 
Cm 

U 

a: 

Cm 

CO 

►4 

M 

Cm 

M 

2 

a 

Eh 

to 

Cl) 

M 

Cm 
> 

Eh 

< 

8 

Cm 
>^ 
CJ 

a 

Eh 
CO 

en 

CO 

►J 

M 

Cm 

> 

CO 
Eh 

< 

M-l 
CO 

M 

Cm 

CO 
Eh 
i-1 

§ 

Cm 

Im 
CO 

M 

Cm 

CO 
Eh 

t— 1 

CO 

"> 

§ 

Cm 

U-l 

CO 

.4 

M 

Cm 

U-l 
CO 

M 

Cm 

CO 

:«: 

U-l 

CO 

M 

Cm 
CO 

Eh 

CO 

(U 

r-i 

JQ  +> 

<a  c 

(1) 

•H  3 

eH 

U 

H  o 

Xi 

Q) 

(0  o 

nj 

4-" 

> 

•H  Q) 

C 

a 

M  r-{ 

-H 

> 

>i-H 

(T3  -H 

O 

0) 

0)  M 

>  IW 

aj^ 

M    (0 

>  HH  a^  j«i  c 


c 
o 

■M 
4-1 
(0 
)-i 
0) 

a 
o 

0) 

m  oj 
a 
>1 


E 


0) 

a  d) 


<TJ 


•0   1- 


c 

•o  n     cr 
c  o    o» 


(0 
•H 
4J 

C  *J 
0)  u 
3   Q> 

cr  u 

0)  -H 
0)  T3 

>i  >» 

0)   0) 


to  o 

tn  u:  »c 


129 


shows  how  the  different  statement  forms  relate   to   the 
different  combinations  of  file  attributes. 

The  chief  characteristic  of  record  input-output  is 
that  it  involves  a  direct  transfer  of  information  between 
the  dataset  and  addressable  memory,  without  any  formatting 
or  editing.   It  is  therefore  the  programmer's  responsibility 
to  be  sure  that  the  format  of  information  on  the  dataset 
agrees  with  the  format  in  memory,  as  defined  by  the  imple- 
mentation.  If  the  dataset  is  itself  created  by  a  PL/I  pro- 
gram,  this  is  not  too  difficult. 

The  READ- statement  causes  a  single  record  to  be  read 
from  a  specified  file.   It  can  also  be  used  to  skip  records 
on  a  sequential  file.   Its  simplest  form,  applied  to  a 
sequential  file,  is  illustrated  by 

READ  FILE (CUST)  INTO (CUST_INFO) ; 

A  single  record  is  transferred  from  the  dataset  associated 
with  CUST  into  the  variable  CUST_INFO.   If  the  size  of  the 
record  disagrees  with  the  size  of  the  variable,  the  RECORD- 
condition  is  raised  for  the  file.   If  there  are  no  more  records 
left  in  the  dataset,  the  ENDFILE-condition  is  raised  for  the 

file. 

The  destination  of  a  newly  read  record  can  be  specified 
either  by  an  INTO-clause  or  by  a  SET-clause.   If  the  INTO- 
clause  is  given,  then  the  record  is  read  into  a  buffer,  and 
the  pointer  specified  in  the  SET-clause  is  set  to  the  location 
of  the  record  within  the  buffer.   Although  the  SET-clause  is 


130 


less  convenient  to  use  than  the  INTO-clause,  it  has  two 
advantages.   First,  the  SET-clause  can  be  used  to  read 
records  whose  length  is  specified  within  the  record  itself. 
The  INTO-clause  cannot  be  used  for  such  records  because  the 
size  of  the  necessary  variable  is  not  known.   Second,  operat- 
ing  on  the  record  in  the    buffer  avoids   the  need  to  copy 
the  record  from  the  buffer  to  the  variable. 

The  KEY-clause  is  used  to  specify  the  position  within 
the  dataset  of  the  record  to  be  read.   For  instance,  the 
statement 

READ  FILE (EMPL)  INTO (EMPLOYEE_RECORD)  KEY (EMP_NUMBER) ; 

causes  the  record  whose  key  is  the  character-string  form  of 
EMP-NUMBER  to  be  read  into  the  variable  EMPLOYEE_RECORD. 
(If  the  key  value  is  not  a  character  string,  it  is  converted 
to  one.)   If  the  key  designates  a  nonexistent  record,  the  KEY- 
condition  is  raised  for  the  file. 

The  KEYTO-clause,  in  contrast,  does   not  influence  the 
dataset  position  at  all.   Instead  the  key  in  the  record  being 
read  is  assigned  to  the  variable  specified  in  the  KEYTO-clause. 
This  facility  is  necessary  since  the   key  may  not  be  part  of 
the  record  itself.   For  instance,  the  statement 

READ  FILE (EMPL)  INTO (EMPLOYEE_RECORD)  KEYTO (EMP_NUMBER) ; 

causes  the  next  record  from  EMPL  (which  must  be  keyed  sequential) 
to  be  read  into  EMPLOYEE_RECORD,  and  the  key  associated  with 
that  record  to  be  assigned  to  EMP  NUMBER. 


131 


The  IGNORE-clause  can  be  used  in  order  to  skip  records. 
For  instance,  the  statement 

READ  FILE(EMPL)  IGN0RE(2); 

causes  two  records  on  the  dataset  associated  with  EMPL  to  be 
skipped.  (EMPL  must  necessarily  be  sequential.)   The  IGNORE- 
clause  and  the  KEY-clause  can  be  used  together,  and  if  the 
count  in  the  IGNORE-clause  is  zero  the  effect  is  to  position 
the  dataset  at  the  record  designated  by  the  KEY-clause.  The 
IGNORE-clause  and  the  KEYTO-clause  can  be  used  together  to 
read  a  key  without  reading  the  associated  record. 

The  WRITE-statement  can  be  used  only  with  output  or  direct 
update  files.   It  causes  the  variable  named  in  the  FROM-clause 
to  be  written  onto  the  dataset.   If  the  dataset  is  sequential, 
the  new  record  is  written  at  its  end;  otherwise  the  position 
of  the    new  record  is  arbitrary.   The  KEYFROM-clause,  which 
specifies  the    key  of  the   new  record,  must  be  used  if  the  file 
is  keyed. 

The  LOCATE- statement  can  be  used  only  with  output  files. 
It  is  analogous  to  the  READ- statement  with  the  SET-option. 
Execution  of  a  LOCATE-statement  causes  space  for  the  variable 
named  in  the  statement  to  be  allocated  in  an  output  buffer, 
and  then  causes  the  pointer  named  in  the  SET-clause  to  be  set 
to  the  location  of  this  space.   If  the  variable  is  declared 
with  a  pointer,  then  the  SET-clause  can  be  omitted  and  the 
pointer  obtained  from  the  declaration  will  be  used.  For 
instance,  given  the  statements 


132 


DECLARE  RECSPACE  CHARACTER ( 20  0)  BASED (CHPTR) ; 
DECLARE  CHPTR  POINTER; 

the  statements 

LOCATE  RECSPACE  FILE (NEWSET) ; 
and 

LOCATE  RECSPACE  FILE (NEWSET)  SET (CHPTR) ; 

are  equivalent.   After  execution  of  a  LOCATE- statement ,  the 
programmer  can  construct  a  record  within  the  buffer,  referenc- 
ing the  record  using  the  variable  and  pointer  specified  in 
the  LOCATE- statement.   The  record  is  written  when  either 
another  LOCATE-statement  or  a  WRITE-statement  is  executed 
for  the  file.   Closing  the  file  after  executing  the  LOCATE- 
statement  also  causes  the  record  to  be  written. 

The  REWRITE-statement  can  only  be  used  with  update  files; 
it  causes  a  record  in  the  dataset  to  be  replaced.  The  FROM- 
option  can  be  omitted  if  the  preceding  operation  on  the  file 
was  a  READ  with  the  SET-clause;  in  this  case  the  record  just 
read  (which  is  assumed  to  have  been  modified)  replaces  its  old 
copy   in  the  dataset.   Otherwise  the  FROM-clause  specifies  the 
source  of  the  replacement  record,  and  the  KEY-clause,  if  given, 
specifies  which  record  is  to  be  replaced.   If  the  KEY-clause 
is  not  given,  then  the  file  must  be  sequential,  and  the  record 
at  the  current  position  in  the  file  is  replaced.   For  the 
replacement  to  be  acceptable,  the  current  position  must  be 
well-defined,  and  the  preceding  operation  on  the  file  must  not 
have  been  a  DELETE. 


133 


The  DELETE- statement,  like  the  REWRITE-statement ,  can  only 
be  used  with  update  files.   If  a  key  is  specified,  then  the 
record  with  that  key  is  deleted.   Otherwise  the  behavior  is 
similar  to  that  of  REWRITE:   the  record  at  the  current  position 
is  deleted;  the  current  position  must  be  well-defined;  and  the 
preceding  operation  must  not  have  been  another  DELETE . 


134 


STREAM  INPUT-OUTPUT 

Stream  input-output  differs  from  record  input-output  in 
that  a  transformation  is  performed  when  information  is  moved 
between  a  generation  of  storage  and  a  dataset.   On  output,  the 
transformation  consists  of  translating  a  data  value  (which 
must  be  of  a  printable  type)  into  a  character  representation 
of  that  value;  on  input,  the  transformation  goes  in  the  opposite 
direction.   The  character  representation  need  not  represent 
the  value  directly,  since  formatting  conventions  can  be  used. 
For  instance,  the  character  sequence  "4  387"  could  represent 
the  value  4  3.87  on  either  input  or  output,  were  an  appropriate 
format  to  be  used.   The  GET-statement  is  used  for   stream 
input,  while  the  PUT-statement  is  used  for  stream  output. 
Each  of  these  statements  has  three  variants:  list-directed, 
data-directed,  and  edit-directed .   With  the  exception  of  a  few 
pathological   cases,  input  is  the  inverse  of  output.  Thus,  if 
information  is  written  by  a  PUT-statement  and  later  read  by  a 
GET-statement  of  the  same  form,  the   original  values  in  storage 
will  be  unchanged.   However,  it  is  not  in  general  true  that 
reading  from  a  file  and  then  writing  what  was  read  will  yield 
the  contents  of  the  original  file. 

The  file  to  be  operated  on  by  a  GET-statement  or  a  PUT- 
statement  can  be  specified  either  explicitly  or  implicitly. 
The  statement 

PUT  FILE (TABLES) LIST (A, B) ; 

causes  the  printable  representation  of  the  values  of  A  and  B 


135 


to  be  written  onto  the  file  TABLES.  If  the  FILE-clause  is 
omitted,  the  standard  input  file  SYSIN  is  assumed  for  GET- 
statements,  and  the  standard  output  file  SYSPRINT  (a  print 
file)  is  assumed  for  PUT-statements .     > 

The  stream  input-output  statements  can  be  used  to  encode 
and  decode  strings  in  storage  by  means  of  the  STRING-clause. 
For  example,  the  statement 

GET  STRING (STR)  LIST(A,B); 

causes  the  values  of  the  variables  A  and  B  to  be  "read"  from 
the  string  STR,  just  as  though  STR  was  a  sequence  of  characters 
on  an  input  file.   Similarly, 

PUT  STRING(STR)  LIST(A,B); 

causes  the  values  of  A  and  B  to  be  converted  to  their  printable 
representations,  and  then  causes  the  sequence  of  representations 
(with  an  intervening   blank)  to  be  assigned  to  the  character 
variable  STR. 

Line  and  page  skips  can  be  specified  in  the  GET-statement 
and  the  PUT- statement ,  although  certain  forms  are  applicable 
only  to  a  PUT-statement  that  designates  a  print   file.  For 
instance, 

PUT  SKIP (2)  LIST(A,B); 

causes  two  lines  to  be   skipped  before  A  and  B  are  printed,  while 

PUT  PAGE  LIST (A, B) ; 

causes  a   new  page  to  be  started  before  A  and  B  are  printed. 
The  statement 

136 


PUTLINE(2)  LIST{A,B); 

causes  the  printer  to  be  positioned  to  the  second  line  on  the 
page  before  A  and  B  are  printed;  it  differs  from  SKIP  in  that 
it  selects  an  absolute  page  position  rather  than  a  page  posi- 
tion relative  to  the  previous  line.   PAGE  and  LINE  can  only 
be  specified  for  print  files.   SKIP  on  an  input  file  causes 
the  remainder  of  the  input  line  to  be  skipped. 

Data  Lists 

A  GET-statement  or  a   PUT-statement  can  contain   one  or 
more  data    lists,    specifying  the  items  to  be  read  or  written. 
In  its  simplest  form,  the  data  list  is  merely  a  sequence  of 
scalars,   e.g. 

PUT  LIST (RATE, TIME, RATE*TIME) ; 

As  this  example  shows,  expressions  as  well  as  variables  can 
appear  in  the  data  list  of   a  PUT-statement.   The  it6ms  in 
the  data  list  of  a  GET-statement,  however,  have  the  same 
restrictions  as  the  targets  of  an  assignment-statement,  since 
one  cannot  read  a  value  into  an  expression.   A  data  list  can 
also  contain  iterations,  e.g., 

PUT  LIST((I,F(I)  DO  I  =  1  TO  FNLIMIT) ) ; 

Since  any  item  can   itself  be  an  iteration,  iterations  can  be 

nested  to  any  depth.   The  DO  that  controls  the  statement  is 

subject  to  the  same  restrictions  as  the  specif ied-DO-statement. 

A  GET-statement  can  use  an  input  value  as  an  iteration 

count,  e.g., 

137 


GET  LIST( (N,  (COST(K)  DO  K  =  1  TO  N) )  ; 

Moreover,  aggregates  can  be   included  in  the  data  list,  e.g., 

DECLARE  MIX(40,40)  FIXED; 

PUT  LIST( (MIX(*,I)  DO  I  =  1  TO  40)); 

In  this  example  the  elements  of  MIX  will  be  printed  out  with 

the  first  subscript  varying  most  rapidly,  as  in  Fortran.  On 

the  other  hand,  the  statement 

PUT  LIST  (MIX)  ; 

with  MIX  declared  as  above  will  print  out  the  elements  of  MIX 
with  the  last  subscript  varying  most  rapidly  (since  this  is  the 
implicit  order  in  storage,  as  defined  by  the  rules  of  PL/I). 

List-Directed  Input-Output 

The  GET  LIST-statement  reads  an  unformatted  sequence  of 
items  from  the  input  stream.   The  data  to  be  read  consists  of 
a  list  of  items  separated  by  blanks  or  commas,  e.g., 

23,47, 'DAVID  DAVIS' 

Since  the  dataset  is    left  positioned  after  the  last  character 
that  was  read,  it  is  possible  to  read  items  from  the  same  line 
using  several  separate  GET  LIST-statements   in  succession.  The 
successive  items  on  the  dataset  are  assigned  to  the  successive 
items  in  the  data  list.   If  the  data  list  contains  an  aggregate, 
then  enough  items  are  read  from  the  dataset  to  fill  the  aggre- 
gate.  The  dataset  can  also  indicate  empty  items  by   means  of 
two  commas  in  a  row.   For  instance,  the   statement 


138 


GET  LIST(M1,M2,M3) ; 
applied  to  the  input 

22, ,891 

will  cause  Ml  to  be  set  to  22,  M3  to  be  set  to  891,  and  M2  to 
be  left  unchanged. 

The  types  of  the  items  read  from  the  dataset  need  not 
agree  with  the  types  of  the  items  in  the  data  list;  if  there 
is  any  disagreement,  the  item  read  is  converted  to  the  type 
of  the  item  in  the  data  list.   For  instance,  the  input  value 
for  a  float  variable  can  be  written  as  an  integer.   A 
character-string  item  on  the  dataset  need  not  be  quoted 
unless  it  contains  a  comma  or  blank  or  starts  with  a  quote. 
Thus 

THIS  ISN'T  BAD 

can  be  read  into  a  list  of  three  character  variables,  and  the 
variables  will  receive  the  strings  exactly  as  written. 
However,  if  the  dataset  contains  the  item 

'ISN' 'T' 

the  usual  rules  for  interpreting  a  character-string  constant 
will  be  applied  and  the  receiving  variables  will  receive  the 
value 

ISN'T 

The  PUT  LIST-statement  writes  a  sequence  of  items  onto 
the  specified  output  file.  Successive  items  arc  -d  at 
successive  tabstop  positions,  and  when  a  line  is  filled  (as 


139 


defined  by  the  linesize  for  the  dataset)   a  new  line  is 
started.   If  the  output  file  is  not  a  print  file,  however, 
items  are  separated  by  single  blanks  rather  than  placed  at 
tabstops.   Thus  the  effect  of 

PUT  SKIP  LIST((N  DO  I  =  1  TO  10)); 

might  be  to  print  the   lines 

12     3     4     5 

6     7     8     9    10 

under  some  appropriate  assumptions  about  the  linesize  and 
tabstop  positions.  Since  a  PUT  LIST-statement  does  not  force 
an  end  of  line,  several  PUT  LIST-statements   can  place  output 
onto  the  same  line. 

Data-Directed  Input-Output 

The  GET  DATA- statement  reads  a  sequence  of  variable  names 
and  associated  values,  e.g., 

A=3     B=12     D=0; 

The  pairs  in  this  sequence  can  be  separated  by  either  blanks 
or  commas  (as  with  the  GET  LIST-statement) ,  and  the  sequence  is 
ended  by  a  semicolon.   The  GET  DATA-statement  itself  can,  but 
need  not,  specify  a  list  of  variables,   e.g., 

GET  DATA(A,B,C,D,E) ; 
or 

GET  DATA; 

The  second  form  is  easier  to  use,  but  it  has  the  disadvantage 


140 


that  it  forces  a  complete  symbol  table  to  be  included  in  the 
compiled  program.   The  items  in  the  input  stream  need  not  be 
given  in  the  same  order  as  the  variables  in  the  list.  More- 
over, variables  in  the  data  list  can  be  repeated  or  omitted 
in  the  input  stream.   Subscripted  and  name-qualified  variables 
can  also  be  included  in  the  input  stream,  although  name 
qualifications  must  be  complete.   For  instance, 

A (3, 7)  =  'JOE',  A (4, 7)  =  'SAM',  ST.COUV  =  19.3; 

is  a  valid  input  line,  assuming  an  appropriate  data  list. 

The  PUT  DATA-statement  writes  a  list  of  variables, 
together  with  their  values,  onto  the  output  stream.  As  with 
the  GET  DATA-statement,  the  PUT  DATA-statement  need  not 
contain  a  list.   Execution  of  the  statement 

PUT  DATA; 
causes  the  values  of  all    printable  variables  to  be  written 
onto  the  output  stream.   Otherwise,  the    listed  variables 
are  written  out.   If  any  of  these  variables  are  aggregates, 
the  scalar  elements  of   the  aggregate  are  written  out, 
using  fully-qualified  names  and  appropriate  subscripts. 
Unlike  the    other  two  forms  of   the  PUT-statement ,  the 
PUT  DATA-statement  cannot  include  expressions  in  the  data 
list,  since  expressions  do  not  have  names.   If  the  output 
file  is  a  print  file,  successive  items  are  placed  at  succes- 
sive  tabstops.   Thus,  assuming  appropriate  stored  values  and 
tabstops,  the  effect  of  the  statements 


141 


DECLARE 

1  AGG(3) 

2  (RED, BLUE)  FIXED  DECIMAL(2); 
PUT  DATA(AGG) ; 

is  to  print  out  the   lines 

AGG.RED(l)  =   4    AGG.BLUE(l)  =  8    AGG.RED(2)   =  0 
AGG.BLUE(2)=  -5    AGG.RED(3)   =  7    AGG.BLUE(3)  =  1; 


Edit-Directed  Input-Output 

Edit-directed  input-output  is  accomplished  through  the 
GET  and  PUT  EDIT-statements .   For  edit-directed  input-output, 
the  transformation  between  the  internal  and  external  forms 
of  the  data  is  governed  by  a  format    list.      When  an  item  in 
a  data  list  is  read,  a  format  is  obtained  from  the  format 
list  and  used  to  transform  the  character  representation  of 
the  item,  as  it  appears  in  the  input  stream,  into  a  stored 
value.   When  an  item  in  a  data  list  is  written,  a  format  is 
used  to  transform  the  value  of  the  item  into  a  sequence  of 
characters  to  be  inserted  into  the  output  stream.  For  example, 
the  statement 

GET  EDIT (I, J)  (F(4),F(3)); 

applied  to  the  input  stream 

^]625.00]6)6]63.1A 

causes  the  variables  I  and  J  to  be  assigned  the  values  15 
(obtained  from  the  first  four  characters  of  the  stream)  and 
3  (obtained  from  the  next  three  characters   of  the  stream) . 
Similarly, 

142 


PUT   EDIT(25, 3.142)     (2F(7,2)); 

causes   the   characters 

]6]625.00]6]6\63.14 

to  be  placed  into  the  output  stream.   In  this  case,  the  2  in 
front  of  the  format  item  F(7,2)  indicates  that  the  item  is 
to  be  repeated  twice. 

The  available  formats  are  listed  in  Table  12.   There 
are  two  kinds  of  formats:   control  formats  and   data  formats. 
Control  formats  control  positioning  of  the  dataset;  they 
cause  skipping  of  information  on  input,  and  generation  of 
blanks,  linemarks,  pagemarks,  and  carriage-returns  on  output. 
When  a  format  list  is  interpreted,  control  formats  are  executed 
as  they  are  encountered;  a  control   format  does  not  use  up  an 
item  from  the  data  list.   A  data  format,  on  the  other  hand, 
requires  a  corresponding  item  in  the  data  list,  and  the 
format  item  together  with  the  data  item  determines  what  action 
is  to  be  taken. 

When  the  data  list  is  exhausted,  interpretation  of  the 
format  list  ceases.   For  instance,  execution  of  the  statement 

PUT  EDIT (J)   (E(9),SKIP); 

does  not  cause  the  control  format  SKIP  to  be  executed,  since 
after  J  is  paired  with  E(9)  the  data  list  is  exhausted  and 
execution  of  the  statement  is  complete.   If  the  format  list 
is  exhausted  while  items  still  remain  in  th'   '  '  i  list,  then 
interpretation  of  the  format  list  starts  over  again  from  the 


143 


Table    12.      Format   Types 

(1)  Data  Formats 

A(u)  alphanumeric  with  field  width  u 

(w   can  be    omitted  on  output) 
B(w)  bitstring   with  field  length  w 

Bl(u)         (w  can  be  omitted  on  output; 
B2(u)         31  indicates  base  2,   32  indicates  base  4, 
B3(w)         B3  indicates  base  8,  and 
B4 (u)         B4  indicates  base  16) 
F(w,d,s)  fixed  with  field  width  w,    d   digits  to  right 

of  decimal  point,  scaling  s 

{d   and  s    are  optional) 
E{w,d,s)  float  with  field  width  u,  d   digits  to  right 

of  decimal  point  in  mantissa,  s  digits  in 

mantissa  (d   and  s    are  optional) 
P  pia  pictured  according  to  picture  pia 

C(fl,fl)  complex  with  real  part  formatted  using  fl, 

imaginary  part  formatted  using  f2 

{fl    assumed  the  same  as  fl    if  f2   omttted) 

(2)  Control  Formats 

X(w)  blank  or  ignore  field  of  width  w 

COLUMN (n)  continue  reading  or  writing  at  column  position  n 

TAB(n)  skip  n  tabstops 

SKIP(n)  skip  n  lines 

LINE(n)  position   at  nth  line  of  printed  page 

PAGE  start  new  printed  page 


(3)   Remote  Format 

R(ref)  use  format  obtained  by  evaluating 

reference  r-ef 


144 


Table   13. 

Examples 

of 

Input 

Formats 

Input   Field 

Format 

Value 

TWOfe$CATS 

A(8) 

TWO)!JCAT£ 

1011 

B(4) 

'lOll'B 

ONE 

B(3) 

none   — 

CONVERSION 

raised 

)z{)z$1011)z5 

B(7) 

'lOll'B 

2437 

33(4) 

•OlOlOOOlllll'B 

2437 

F(4) 

2437 

lz{2437JzJ 

F(6) 

2437 

)z52437)z$ 

F(6,  1) 

243.7 

.2437 

F(4,l) 

.2437 

2437 

F(4,l,2) 

24370 

CATS 

F(4,l,2) 

. 

none   — 

CONVERSION 

raised 

2437E1 

F(6) 

none    -- 

CONVERSION 

raised 

2437 

E(4) 

2437E0 

2437E-2 

E(7) 

24.37E0 

2437 

E(4,l) 

243. 7E0 

2437E-2 

E(4,l) 

2.437E0 

2.437E-2 

E(5,l) 

243. 7E0 

2437E-2 

E(4,l,l) 

2.437E0 

WrfM 

F(3) 

0 

Wb$ 

E(3) 

0 

W2M>1 

P    '  (6)Z' 

2437 

2431W 

P     '  (6)Z' 

none   -- 

CONVERSION 

raised 

2437 

C(F(2)) 

24+371 

-2437 

C(F(3,1) 

F(2)  ) 

-2.4+371 

145 


Table    14. 

Examples   of 

Outp' 

at   Formats 

Value 

Format 

Output   Field 

DYNASTY 

A 

DYNASTY 

2.48 

A 

}z{)zJ2.48 

DYNASTY 

A(6) 

DYNAST 

DYNASTY 

A(10) 

DYNASTYl!5)z5JzJ 

•lOllOOl'B 

B 

1011001 

'lOllOOl'B 

B3 

544 

■IIOIOOI'B 

B4 

D2 

•IIOIOOI'B 

B4(5) 

D2Jz$)z$)zi 

'IIOIOOI'B 

B4(l) 

D    (and      STRINGSIZE    raised) 

2.48 

F(6) 

]^\6\6)^)62 

2.48 

F(6,l) 

W]ii2.5 

2.48 

F{6,3,1) 

)z50.248 

-25 

F(2) 

none    —    SIZE    raised 

24.86 

E(13) 

]6]6]62.48eE+001 

24.86 

E(13,0) 

]6]6)6\ii2A8eE-002 

-24.86 

E(13,l,3) 

)z5M)z5-2  4.9E+0  00 

2.48 

P    •$$$V.99 

1 

\6$2.48 

17.9+61 

C(F(6,1)) 

]6]611 .9W\66.0 

-17.9+61 

C(F(6,1) ,F(6)) 

]6-n  .9]6WW^ 

146 


beginning.   For  instance, 

DECLARE  A{*)  FLOAT; 

PUT  EDIT (A)   (SKIP,  6E(14,5)); 

causes  the  contents  of  the  array  A  to  be  written  out  with 
six  values  on  a  line.   As  many  lines  as  necessary  are  used. 
A  format  list  can  contain  repeated   items  and  repeated 
lists.   The  repetition  counts  and  parameters  in  a  format  list 
are  not  limited  to  constants;  they  can  be  arbitrary  expres- 
sions, as  in  the  format  list 

( (M)E(14,11-K) , (N) (A(L) ,X(5) ,A{L+1) )) 

Repetition  counts  that  are  not  integer  constants  must 
be  written  in  parentheses. 

An  edit-directed  statement  can  have   more  than  one  pair 
of  data  lists  and  format  lists,  e.g. 

PUT  EDIT(A(*,3))   (SKIP,10E(14,DECPT) ) 
(A(*,4))   (SKIP,6E(14,DECPT) ; 

The  behavior  of  input  formats  applied  to  various  data  values 
is  illustrated  in  Table  13.   Except  for  the  P-format  and  the 
C-format,  all  of  the  input  formats  specify  a  field  width, 
which  is  the  number  of  characters  to  be  read  from  the  input 
stream.   (The  P-format  and  C-format  specify  the  field  width 
implicitly.)   For  the  B-formats,  the  E-format,  and  the  F-format, 
the   numerical  value  to  be  read  can  have  leading  and  trailing 
blanks,  which  are  ignored.   Linemarks   within  a  field  are 
ignored,  so  that  a  field  can  be  split  over  two  lines.   For  the 
F-format  and  the  E-format,  the  second  parameter,  if  present, 

147 


indicates  where  an  implicit  decimal  point  should  be  inserted 
if  none  appears  explicitly  in  the  input.   For  the  F-format, 
the  third  parameter  specifies  scaling  to  be  applied  to  the 
input  value,  while  the  third  parameter  of  an  E-format  is 
ignored. 

The  behavior  of  output  formats  is  shown  in  Table  14. 
The  output  formats  have  behavior  inverse  to  the  input  formats . 
The  rules  are  not  entirely  inverse,  however,  since  the  input 
formats  assign  meaning  to  certain  fields  that  cannot  be  pro- 
duced by  the  corresponding  output  formats.   For  instance,  an 
output  F-format  cannot  produce  trailing  blanks,  but  an  input 
F-format  can  read  trailing  blanks.   Moreover,  unsealed  output 
fields  always  have  explicit  decimal  points  unless  they  are 
integers,  but  unsealed  input  fields  may  have  implicit  decimal 
points.   Another  difference   is  that  on  output,  the  A- format 
and  the  B-formats  need  not  contain  field  widths,  since  a  field 
width  can  be  deduced  by  converting  the  output  value  to  character 
type  or  to  bit  type   as  appropriate. 

A  remote  format  can  appear  in  a  format  list,  either  as 
the  only  item  in  the  list  or  in  combination  with  other  items. 
The  format  item  R{ref)    is  interpreted  by  evaluating  the  reference 
ref,    which  must  be  format-valued,  i.e.,  it  must  evaluate  to  the 
statement-name  of  a  FORMAT-statement .   The  contents  of  that 
FORMAT- statement   are  then  interpreted.  For  instance,  the  effect 
of  the  statements 

GET  EDIT(A,B,C)   (F ( 3) , R (FF) , F { 2) ) ; 
FF:   FORMAT (F( 5) ) ; 

148 


is  the  same  as  that  of  the  statement 

GET  EDIT(A,B,C)  (F  (  3)  ,  F  (  5)  ,  F  (  2)  )  ; 

Remote  formats  are  useful  when  the  same  format  is  to  be  used 
in  a  number  of  different  PUT  EDIT  or  GED  EDIT-statements. 
The  same   format  can  be  used  for  both  input  and  output. 


149 


BIBLIOGRAPHY 

1.  American  National  Standards  Institute,  "American 
National  Standard  -  Programming  Language  PL/I," 
Report  ANSI  X3. 53-1976,  New  York. 

2.  Beech,  D. ,  A  Structural  View  of  PL/I,  ACM  Computing 
Surveys  (2)1,  March  1970,  pp.  33-64. 

3.  Beech,  D. ,  and  Marcotty,  M. ,  Unfurling  the  PL/I  Standard, 
SIGPLAN  Notices  (8)10,  October  1973,  pp.  12-43. 

4.  Frieburghouse,  R. ,  The  MULTICS  PL/I  Compiler,  1969  Fall 
Joint  Computer  Conference  (35),  AFIPS  Press,  Montvale, 
New  Jersey,  pp.  187-199. 

5.  Honeywell  Information  Systems,  Inc.,   "PL/I  Language 
Manual,"  Cambridge,  Massachusetts,  1974. 

6.  IBM  Corporation,  "PL/I  Language  Specifications," 
Number  GY33-600 3-2 ,  1970. 

7.  Lucas,  P.,  and  K.  Walk,   On  the  Formal  Definition  of  PL/I, 
Annual  Review  in  Automatic  Programming  (6)3,  1969, 
Pergamon  Press,  pp.  105-181. 

8.  Pollack,  S.,  and  Sterling,  T. ,  "A  Guide  to  PL/I," 

Second  Edition,  Holt,  Reinhart  and  Winston,  New  York,  1976 

9.  Radin,  G.,  and  Rogoway,  H.,  NPL:  Highlights  of  a  New 
Programming  Language,  Communications  of  the  ACM  (8)1, 
January  1965,  pp.  9-17. 


150 


This  report  was  prepared  as  an  account  of 
Government  sponsored  work.   Neither  the 
United  States,  nor  the  Administration, 
nor  any  person  acting  on  behalf  of  the 
Administration: 

A.  Makes  any  warranty  or  representation, 
express  or  implied,  with  respect  to  the 
accuracy,  completeness,  or  usefulness  of 
the  information  contained  in  this  report, 
or  that  the  use  of  any  information, 
apparatus,  method,  or  process  disclosed 
in  this  report  may  not  infringe  privately 
owned  rights;  or 

B.  Assumes  any  liabilities  with  respect  to 
the  use  of,  or  for  damages  resulting  from 
the  use  of  any  information,  apparatus, 
method,  or  process  disclosed  in  this 
report . 

As  used  in  the  above,  "person  acting  on  behalf 
of  the  Administration"  Includes  any  employee 
or  contractor  of  the  Administration,  or 
employee  of  such  contractor,  to  the  extent 
that  such  employee  or  contractor  of  the 
Administration,  or  employee  of  such  contractor 
prepares,  disseminates,  or  provides  access  to, 
any  information  pursuant  to  his  employment  or 
contract  with  the  Administration,  or  his 
employment  with  such  contractor. 


151